diff --git a/experimental/material_3_demo/lib/component_screen.dart b/experimental/material_3_demo/lib/component_screen.dart index ff3b9beab..20d6c54c0 100644 --- a/experimental/material_3_demo/lib/component_screen.dart +++ b/experimental/material_3_demo/lib/component_screen.dart @@ -140,8 +140,8 @@ class Navigation extends StatelessWidget { ), NavigationDrawers(scaffoldKey: scaffoldKey), const NavigationRails(), - // TODO: Add Search https://github.com/flutter/flutter/issues/117483 const Tabs(), + const SearchAnchors(), const TopAppBars(), ]); } @@ -2182,6 +2182,101 @@ class _SlidersState extends State { } } +class SearchAnchors extends StatefulWidget { + const SearchAnchors({super.key}); + + @override + State createState() => _SearchAnchorsState(); +} + +class _SearchAnchorsState extends State { + String? selectedColor; + List searchHistory = []; + + Iterable getHistoryList(SearchController controller) { + return searchHistory.map((color) => ListTile( + leading: const Icon(Icons.history), + title: Text(color.label), + trailing: IconButton( + icon: const Icon(Icons.call_missed), + onPressed: () { + controller.text = color.label; + controller.selection = + TextSelection.collapsed(offset: controller.text.length); + }), + onTap: () { + controller.closeView(color.label); + handleSelection(color); + }, + )); + } + + Iterable getSuggestions(SearchController controller) { + final String input = controller.value.text; + return ColorItem.values + .where((color) => color.label.contains(input)) + .map((filteredColor) => ListTile( + leading: CircleAvatar(backgroundColor: filteredColor.color), + title: Text(filteredColor.label), + trailing: IconButton( + icon: const Icon(Icons.call_missed), + onPressed: () { + controller.text = filteredColor.label; + controller.selection = + TextSelection.collapsed(offset: controller.text.length); + }), + onTap: () { + controller.closeView(filteredColor.label); + handleSelection(filteredColor); + }, + )); + } + + void handleSelection(ColorItem color) { + setState(() { + selectedColor = color.label; + if (searchHistory.length >= 5) { + searchHistory.removeLast(); + } + searchHistory.insert(0, color); + }); + } + + @override + Widget build(BuildContext context) { + return ComponentDecoration( + label: 'Search', + tooltipMessage: 'Use SearchAnchor or SearchAnchor.bar', + child: Column( + children: [ + SearchAnchor.bar( + barHintText: 'Search colors', + suggestionsBuilder: (context, controller) { + if (controller.text.isEmpty) { + if (searchHistory.isNotEmpty) { + return getHistoryList(controller); + } + return [ + const Center( + child: Text('No search history.', + style: TextStyle(color: Colors.grey)), + ) + ]; + } + return getSuggestions(controller); + }, + ), + const SizedBox(height: 20), + if (selectedColor == null) + const Text('Select a color') + else + Text('Last selected color is $selectedColor') + ], + ), + ); + } +} + class ComponentDecoration extends StatefulWidget { const ComponentDecoration({ super.key, @@ -2291,3 +2386,26 @@ class ComponentGroupDecoration extends StatelessWidget { ); } } + +enum ColorItem { + red('red', Colors.red), + orange('orange', Colors.orange), + yellow('yellow', Colors.yellow), + green('green', Colors.green), + blue('blue', Colors.blue), + indigo('indigo', Colors.indigo), + violet('violet', Color(0xFF8F00FF)), + purple('purple', Colors.purple), + pink('pink', Colors.pink), + silver('silver', Color(0xFF808080)), + gold('gold', Color(0xFFFFD700)), + beige('beige', Color(0xFFF5F5DC)), + brown('brown', Colors.brown), + grey('grey', Colors.grey), + black('black', Colors.black), + white('white', Colors.white); + + const ColorItem(this.label, this.color); + final String label; + final Color color; +} diff --git a/experimental/material_3_demo/test/component_screen_test.dart b/experimental/material_3_demo/test/component_screen_test.dart index c027ce1b1..c22397f17 100644 --- a/experimental/material_3_demo/test/component_screen_test.dart +++ b/experimental/material_3_demo/test/component_screen_test.dart @@ -93,6 +93,9 @@ void main() { // Tabs expect(find.byType(TabBar), findsOneWidget); + // Search + expect(find.byType(SearchBar), findsOneWidget); + // Top app bars expect(find.byType(AppBar), findsNWidgets(6));