Created
July 30, 2018 18:35
-
-
Save rodydavis/045e5c12d164540fa2430b8ea0faba80 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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>[ | |
const Text('Press the '), | |
const Tooltip( | |
message: 'search', | |
child: const Icon( | |
Icons.search, | |
size: 18.0, | |
), | |
), | |
const 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: const Text('Peter Widget'), | |
accountEmail: const Text('peter.widget@example.com'), | |
currentAccountPicture: const CircleAvatar( | |
backgroundImage: const 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: const Icon(Icons.payment), | |
title: const 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); | |
}, | |
); | |
}, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment