/multi_trigger_autocomplete

A flutter widget to add trigger based autocomplete functionality to your app.

Primary LanguageDartMIT LicenseMIT

Multi Trigger Autocomplete

Open Source Love License Dart CI CodeCov Version

A flutter widget to add trigger based autocomplete functionality to your app.

Show some ❤️ and star the repo to support the project

An animated image of the MultiTriggerAutocomplete

Installation

Add the following to your pubspec.yaml and replace [version] with the latest version:

dependencies:
  multi_trigger_autocomplete: ^[version]

Usage

To use this package you must first wrap your top most widget with Portal as this package uses flutter_portal to show the options view.

(Credits to: Remi Rousselet)

Portal, is the equivalent of [Overlay].

This widget will need to be inserted above the widget that needs to render under your overlays.

If you want to display your overlays on the top of everything, a good place to insert that Portal is above MaterialApp:

Portal(
  child: MaterialApp(
    ...
  )
);

(works for CupertinoApp too)

This way Portal will render above everything. But you could place it somewhere else to change the clip behavior.

Import the package:

import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart';

Use the widget:

MultiTriggerAutocomplete(
  optionsAlignment: OptionsAlignment.topStart,
  autocompleteTriggers: [
    // Add the triggers you want to use for autocomplete
    AutocompleteTrigger(
      trigger: '@',
      optionsViewBuilder: (context, autocompleteQuery, controller) {
        return MentionAutocompleteOptions(
          query: autocompleteQuery.query,
          onMentionUserTap: (user) {
            final autocomplete = MultiTriggerAutocomplete.of(context);
            return autocomplete.acceptAutocompleteOption(user.id);
          },
        );
      },
    ),
    AutocompleteTrigger(
      trigger: '#',
      optionsViewBuilder: (context, autocompleteQuery, controller) {
        return HashtagAutocompleteOptions(
          query: autocompleteQuery.query,
          onHashtagTap: (hashtag) {
            final autocomplete = MultiTriggerAutocomplete.of(context);
            return autocomplete
                .acceptAutocompleteOption(hashtag.name);
          },
        );
      },
    ),
    AutocompleteTrigger(
      trigger: ':',
      optionsViewBuilder: (context, autocompleteQuery, controller) {
        return EmojiAutocompleteOptions(
          query: autocompleteQuery.query,
          onEmojiTap: (emoji) {
            final autocomplete = MultiTriggerAutocomplete.of(context);
            return autocomplete.acceptAutocompleteOption(
              emoji.char,
              // Passing false as we don't want the trigger [:] to
              // get prefixed to the option in case of emoji.
              keepTrigger: false,
            );
          },
        );
      },
    ),
  ],
  // Add the text field widget you want to use for autocomplete
  fieldViewBuilder: (context, controller, focusNode) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ChatMessageTextField(
        focusNode: focusNode,
        controller: controller,
      ),
    );
  },
),

Demo

Mention Autocomplete Hashtag Autocomplete Emoji Autocomplete
Mention Autocomplete Hashtag Autocomplete Emoji Autocomplete

Customization

MultiTriggerAutocomplete

MultiTriggerAutocomplete(
  // Defines the autocomplete trigger that will be used to match the
  // text.
  autocompleteTriggers: autocompleteTriggers,
  
  // Defines the alignment of the options view relative to the
  // fieldView.
  //
  // By default, the options view is aligned to the bottom of the
  // fieldView.
  optionsAlignment: OptionsAlignment.topStart,
  
  // Defines the width to make the options as a multiple of the width
  // of the fieldView.
  //
  // Setting this to 1 makes the options view width matches the width
  // of the fieldView.
  //
  // Use null to remove this constraint.
  optionsWidthFactor: 1.0,
  
  // Defines the duration of the debounce period for the
  // [TextEditingController].
  //
  // This is the time between the last character typed and the matching
  // is performed.
  debounceDuration: const Duration(milliseconds: 350),
  
  // Defines the initial value to set in the internal
  // [TextEditingController].
  //
  // This value will be ignored if [TextEditingController] is provided.
  initialValue: const TextEditingValue(text: 'Hello'),
  
  // Defines the [TextEditingController] that will be used for the
  // fieldView.
  //
  // If this parameter is provided, then [focusNode] must also be
  // provided.
  textEditingController: TextEditingController(text: 'Hello'),
  
  // Defines the [FocusNode] that will be used for the fieldView.
  //
  // If this parameter is provided, then [textEditingController] must
  // also be provided.
  focusNode: FocusNode(),
  
  // Defines the fieldView that will be used to input the text.
  //
  // By default, a [TextFormField] is used.
  fieldViewBuilder: (context, controller, focusNode) {
    return TextField(
      controller: controller,
      focusNode: focusNode,
    );
  },
),

AutocompleteTrigger

AutocompleteTrigger(
  // The trigger string/character that will be used to trigger the
  // autocomplete.
  trigger: '@',
  
  // If true, the [trigger] should only be recognised at
  // the start of the input text.
  //
  // valid example: "@luke hello"
  // invalid example: "Hello @luke"
  triggerOnlyAtStart: false,
  
  // If true, the [trigger] should only be recognised after
  // a space.
  //
  // valid example: "@luke", "Hello @luke"
  // invalid example: "Hello@luke"
  triggerOnlyAfterSpace: true,
  
  // A minimum number of characters can be provided to only show
  // suggestions after the user has input enough characters.
  //
  // example:
  // "Hello @l" -> Shows zero suggestions.
  // "Hello @lu" -> Shows suggestions for @lu.
  minimumRequiredCharacters: 2,
  
  // The options view builder is used to build the options view
  // that will be shown when the [trigger] is detected.
  optionsViewBuilder: (context, autocompleteQuery, controller) {
    return MentionAutocompleteOptions(
      query: autocompleteQuery.query,
      onMentionUserTap: (user) {
        // Accept the autocomplete option.
        final autocomplete = MultiTriggerAutocomplete.of(context);
        return autocomplete.acceptAutocompleteOption(user.id);
      },
    );
  },
)

License

MIT License