| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/material.dart'; |
| |
| class SearchDemo extends StatefulWidget { |
| static const String routeName = '/material/search'; |
| |
| @override |
| _SearchDemoState createState() => new _SearchDemoState(); |
| } |
| |
| class _SearchDemoState extends State<SearchDemo> { |
| final _SearchDemoSearchDelegate _delegate = new _SearchDemoSearchDelegate(); |
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); |
| |
| int _lastIntegerSelected; |
| |
| @override |
| Widget build(BuildContext context) { |
| return new Scaffold( |
| key: _scaffoldKey, |
| appBar: new AppBar( |
| leading: new IconButton( |
| tooltip: 'Navigation menu', |
| icon: new AnimatedIcon( |
| icon: AnimatedIcons.menu_arrow, |
| color: Colors.white, |
| progress: _delegate.transitionAnimation, |
| ), |
| onPressed: () { |
| _scaffoldKey.currentState.openDrawer(); |
| }, |
| ), |
| title: const Text('Numbers'), |
| actions: <Widget>[ |
| new IconButton( |
| tooltip: 'Search', |
| icon: const Icon(Icons.search), |
| onPressed: () async { |
| final int selected = await showSearch<int>( |
| context: context, |
| delegate: _delegate, |
| ); |
| if (selected != null && selected != _lastIntegerSelected) { |
| setState(() { |
| _lastIntegerSelected = selected; |
| }); |
| } |
| }, |
| ), |
| new IconButton( |
| tooltip: 'More (not implemented)', |
| icon: const Icon(Icons.more_vert), |
| onPressed: () {}, |
| ), |
| ], |
| ), |
| body: new Center( |
| child: new Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| new MergeSemantics( |
| child: new Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| new Row( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: const <Widget>[ |
| Text('Press the '), |
| Tooltip( |
| message: 'search', |
| child: Icon( |
| Icons.search, |
| size: 18.0, |
| ), |
| ), |
| Text(' icon in the AppBar'), |
| ], |
| ), |
| const Text('and search for an integer between 0 and 100,000.'), |
| ], |
| ), |
| ), |
| const SizedBox(height: 64.0), |
| new Text('Last selected integer: ${_lastIntegerSelected ?? 'NONE' }.') |
| ], |
| ), |
| ), |
| floatingActionButton: new FloatingActionButton.extended( |
| tooltip: 'Back', // Tests depend on this label to exit the demo. |
| onPressed: () { |
| Navigator.of(context).pop(); |
| }, |
| label: const Text('Close demo'), |
| icon: const Icon(Icons.close), |
| ), |
| drawer: new Drawer( |
| child: new Column( |
| children: <Widget>[ |
| const UserAccountsDrawerHeader( |
| accountName: Text('Peter Widget'), |
| accountEmail: Text('peter.widget@example.com'), |
| currentAccountPicture: CircleAvatar( |
| backgroundImage: AssetImage( |
| 'people/square/peter.png', |
| package: 'flutter_gallery_assets', |
| ), |
| ), |
| margin: EdgeInsets.zero, |
| ), |
| new MediaQuery.removePadding( |
| context: context, |
| // DrawerHeader consumes top MediaQuery padding. |
| removeTop: true, |
| child: const ListTile( |
| leading: Icon(Icons.payment), |
| title: Text('Placeholder'), |
| ), |
| ), |
| ], |
| ), |
| ), |
| ); |
| } |
| } |
| |
| class _SearchDemoSearchDelegate extends SearchDelegate<int> { |
| final List<int> _data = new List<int>.generate(100001, (int i) => i).reversed.toList(); |
| final List<int> _history = <int>[42607, 85604, 66374, 44, 174]; |
| |
| @override |
| Widget buildLeading(BuildContext context) { |
| return new IconButton( |
| tooltip: 'Back', |
| icon: new AnimatedIcon( |
| icon: AnimatedIcons.menu_arrow, |
| progress: transitionAnimation, |
| ), |
| onPressed: () { |
| close(context, null); |
| }, |
| ); |
| } |
| |
| @override |
| Widget buildSuggestions(BuildContext context) { |
| |
| final Iterable<int> suggestions = query.isEmpty |
| ? _history |
| : _data.where((int i) => '$i'.startsWith(query)); |
| |
| return new _SuggestionList( |
| query: query, |
| suggestions: suggestions.map((int i) => '$i').toList(), |
| onSelected: (String suggestion) { |
| query = suggestion; |
| showResults(context); |
| }, |
| ); |
| } |
| |
| @override |
| Widget buildResults(BuildContext context) { |
| final int searched = int.tryParse(query); |
| if (searched == null || !_data.contains(searched)) { |
| return new Center( |
| child: new Text( |
| '"$query"\n is not a valid integer between 0 and 100,000.\nTry again.', |
| textAlign: TextAlign.center, |
| ), |
| ); |
| } |
| |
| return new ListView( |
| children: <Widget>[ |
| new _ResultCard( |
| title: 'This integer', |
| integer: searched, |
| searchDelegate: this, |
| ), |
| new _ResultCard( |
| title: 'Next integer', |
| integer: searched + 1, |
| searchDelegate: this, |
| ), |
| new _ResultCard( |
| title: 'Previous integer', |
| integer: searched - 1, |
| searchDelegate: this, |
| ), |
| ], |
| ); |
| } |
| |
| @override |
| List<Widget> buildActions(BuildContext context) { |
| return <Widget>[ |
| query.isEmpty |
| ? new IconButton( |
| tooltip: 'Voice Search', |
| icon: const Icon(Icons.mic), |
| onPressed: () { |
| query = 'TODO: implement voice input'; |
| }, |
| ) |
| : new IconButton( |
| tooltip: 'Clear', |
| icon: const Icon(Icons.clear), |
| onPressed: () { |
| query = ''; |
| showSuggestions(context); |
| }, |
| ) |
| ]; |
| } |
| } |
| |
| class _ResultCard extends StatelessWidget { |
| const _ResultCard({this.integer, this.title, this.searchDelegate}); |
| |
| final int integer; |
| final String title; |
| final SearchDelegate<int> searchDelegate; |
| |
| @override |
| Widget build(BuildContext context) { |
| final ThemeData theme = Theme.of(context); |
| return new GestureDetector( |
| onTap: () { |
| searchDelegate.close(context, integer); |
| }, |
| child: new Card( |
| child: new Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: new Column( |
| children: <Widget>[ |
| new Text(title), |
| new Text( |
| '$integer', |
| style: theme.textTheme.headline.copyWith(fontSize: 72.0), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ); |
| } |
| } |
| |
| class _SuggestionList extends StatelessWidget { |
| const _SuggestionList({this.suggestions, this.query, this.onSelected}); |
| |
| final List<String> suggestions; |
| final String query; |
| final ValueChanged<String> onSelected; |
| |
| @override |
| Widget build(BuildContext context) { |
| final ThemeData theme = Theme.of(context); |
| return new ListView.builder( |
| itemCount: suggestions.length, |
| itemBuilder: (BuildContext context, int i) { |
| final String suggestion = suggestions[i]; |
| return new ListTile( |
| leading: query.isEmpty ? const Icon(Icons.history) : const Icon(null), |
| title: new RichText( |
| text: new TextSpan( |
| text: suggestion.substring(0, query.length), |
| style: theme.textTheme.subhead.copyWith(fontWeight: FontWeight.bold), |
| children: <TextSpan>[ |
| new TextSpan( |
| text: suggestion.substring(query.length), |
| style: theme.textTheme.subhead, |
| ), |
| ], |
| ), |
| ), |
| onTap: () { |
| onSelected(suggestion); |
| }, |
| ); |
| }, |
| ); |
| } |
| } |