flutter_chips_input_sunny 2.3.0 copy "flutter_chips_input_sunny: ^2.3.0" to clipboard
flutter_chips_input_sunny: ^2.3.0 copied to clipboard

Flutter library for building input fields with InputChips as input options.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_chips_input_sunny/flutter_chips_input.dart';
import 'package:logging/logging.dart';
import 'package:logging_config/logging_config.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Chips Input',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _textController = TextEditingController();
  final GlobalKey<ChipsInputState<AppProfile>> key = GlobalKey();
  ChipsInputController<AppProfile> controller;
  ChipsInputController<AppProfile> controller2;
  FocusNode focusFirst;

  @override
  void initState() {
    super.initState();
    configureLogging(LogConfig.root(Level.FINE, handler: LoggingHandler.dev()));
    controller = ChipsInputController<AppProfile>(findSuggestions: _findSuggestions);
    controller2 = ChipsInputController<AppProfile>(
      findSuggestions: _findSuggestions,
      hideSuggestionOverlay: true,
    );
    controller.queryStream.after.listen((query) {
      _textController.text = query;
    });
    focusFirst = FocusNode(debugLabel: "first chip focus");
  }

  @override
  void dispose() {
    controller.dispose();
    focusFirst.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Chips Input Example'),
      ),
      body: Padding(
        padding: EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            ChipsInput<AppProfile>(
              initialValue: [
                AppProfile(
                    'John Doe', 'jdoe@flutter.io', 'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
              ],
              id: "app-profile",
              controller: controller,
              placeholder: "Search contacts",
              autofocus: true,
              enabled: true,
              maxChips: 5,
              chipTokenizer: (profile) => [profile.name, profile.email].where((token) => token != null),
              onSuggestionTap: (chip) {
                controller.addChip(chip, resetQuery: true);
              },
              onInputAction: (_) {
                if (controller.suggestion.isNotEmpty) {
                  controller.addChip(controller.suggestion.item, resetQuery: true);
                }
              },
              inputConfiguration: TextInputConfiguration(
                autocorrect: false,
              ),
              decoration: InputDecoration(
                // prefixIcon: Icon(Icons.search),
                // hintText: formControl.hint,
                labelText: "Select People",
                // enabled: false,
                // errorText: field.errorText,
              ),
              chipBuilder: (context, _, index, profile) {
                return InputChip(
                  key: ObjectKey(profile),
                  label: Text(profile.name),
                  avatar: CircleAvatar(
                    backgroundImage: NetworkImage(profile.imageUrl),
                  ),
                  onDeleted: () => controller.deleteChip(profile, resetQuery: true),
                  materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
                );
              },
              suggestionBuilder: (context, _, index, profile) {
                return ListTile(
                  key: ObjectKey(profile),
                  leading: CircleAvatar(
                    backgroundImage: NetworkImage(profile.imageUrl),
                  ),
                  title: Text(profile.name),
                  subtitle: Text(profile.email),
                  onTap: () => controller.addChip(profile, resetQuery: true),
                );
              },
            ),
            ChipsInput<AppProfile>(
              initialValue: [
                mockResults[3],
              ],
              id: "app-profile-2",
              controller: controller2,
              placeholder: "Search contacts",
              autofocus: true,
              focusNode: focusFirst,
              enabled: true,
              maxChips: 5,
              chipTokenizer: (profile) => [profile.name, profile.email].where((token) => token != null),
              onSuggestionTap: (chip) {
                controller2.addChip(chip, resetQuery: true);
              },
              onInputAction: (_) {
                if (controller2.suggestion.isNotEmpty) {
                  controller2.addChip(controller2.suggestion.item, resetQuery: true);
                }
              },
              inputConfiguration: TextInputConfiguration(
                autocorrect: false,
              ),
              decoration: InputDecoration(
                // prefixIcon: Icon(Icons.search),
                // hintText: formControl.hint,
                labelText: "No Drop-Down Selector",
                // enabled: false,
                // errorText: field.errorText,
              ),
              chipBuilder: (context, controller, index, profile) {
                return InputChip(
                  key: ObjectKey(profile),
                  label: Text(profile.name),
                  avatar: CircleAvatar(
                    backgroundImage: NetworkImage(profile.imageUrl),
                  ),
                  onDeleted: () => controller.deleteChip(profile, resetQuery: true),
                  materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
                );
              },
              suggestionBuilder: (context, _, index, profile) {
                return ListTile(
                  key: ObjectKey(profile),
                  leading: CircleAvatar(
                    backgroundImage: NetworkImage(profile.imageUrl),
                  ),
                  title: Text(profile.name),
                  subtitle: Text(profile.email),
                  onTap: () => controller2.addChip(profile, resetQuery: true),
                );
              },
            ),
            Wrap(
              spacing: 5,
              children: [
                MaterialButton(
                  elevation: 1,
                  color: Colors.orange,
                  onPressed: () {
                    controller.setQuery("", isInput: false);
                  },
                  child: Text("Reset Search"),
                ),
                MaterialButton(
                  elevation: 1,
                  color: Colors.orange,
                  onPressed: () {
                    controller2.setInlineSuggestion(mockResults[4], notify: true);
                  },
                  child: Text("Set Suggestion"),
                ),
                MaterialButton(
                  elevation: 1,
                  color: Colors.green,
                  onPressed: () {
                    controller.syncChips([
                      mockResults[3],
                      mockResults[7],
                    ], source: ChipChangeOperation.external);
                  },
                  child: Text("Set Chips"),
                ),
                MaterialButton(
                  elevation: 1,
                  color: Colors.green,
                  onPressed: () {
                    controller.syncChips([
                      mockResults[3],
                      mockResults[7],
                      mockResults[6],
                      mockResults[2],
                      mockResults[7],
                    ], source: ChipChangeOperation.external);
                  },
                  child: Text("Set Chips Long"),
                )
              ],
            ),
          ],
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  ChipSuggestions<AppProfile> _findSuggestions(String query) {
    if (query.isNotEmpty) {
      var lowercaseQuery = query.toLowerCase();
      var foundResults = mockResults.where(
        (profile) {
          return profile.name.toLowerCase().contains(lowercaseQuery) ||
              profile.email.toLowerCase().contains(lowercaseQuery);
        },
      ).toList(growable: false)
        ..sort((a, b) =>
            a.name.toLowerCase().indexOf(lowercaseQuery).compareTo(b.name.toLowerCase().indexOf(lowercaseQuery)));
      var exactMatch = mockResults.firstWhere(
        (profile) => profile.name.toLowerCase() == lowercaseQuery || profile.email.toLowerCase() == lowercaseQuery,
        orElse: () => null,
      );
      return ChipSuggestions<AppProfile>(
          suggestions: foundResults,
          match: foundResults.length == 1 && exactMatch != null
              ? Suggestion.highlighted(item: exactMatch, highlightText: exactMatch.name)
              : Suggestion.empty());
    } else {
      return const ChipSuggestions.empty();
    }
  }
}

class AppProfile {
  final String name;
  final String email;
  final String imageUrl;

  const AppProfile(this.name, this.email, this.imageUrl);

  @override
  bool operator ==(Object other) =>
      identical(this, other) || other is AppProfile && runtimeType == other.runtimeType && name == other.name;

  @override
  int get hashCode => name.hashCode;

  @override
  String toString() {
    return name;
  }
}

const mockResults = <AppProfile>[
  AppProfile('John Doe', 'jdoe@flutter.io', 'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
  AppProfile('Paul', 'paul@google.com', 'https://mbtskoudsalg.com/images/person-stock-image-png.png'),
  AppProfile('Fred', 'fred@google.com',
      'https://media.istockphoto.com/photos/feeling-great-about-my-corporate-choices-picture-id507296326'),
  AppProfile('Brian', 'brian@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('John', 'john@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('Thomas', 'thomas@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('Nelly', 'nelly@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('Marie', 'marie@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('Charlie', 'charlie@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('Diana', 'diana@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('Ernie', 'ernie@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
  AppProfile('Gina', 'fred@flutter.io',
      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
];
1
likes
90
pub points
0%
popularity

Publisher

unverified uploader

Flutter library for building input fields with InputChips as input options.

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

after_layout, collection_diff, flutter, logging, observable_collections, stream_transform, sunny_dart

More

Packages that depend on flutter_chips_input_sunny