DraggableScrollableSheet child starts in the middle of the sheet in flutter - flutter-layout

I have a listview in the main scaffold, and I added a gesturedetector in the items to display a bottomsheet for item details. The issue is that the content starts in the middle of the sheet.
this is how the code for the sheet:
showModalBottomSheet(
context: context,
builder: (context) => SizedBox.expand(
child: DraggableScrollableSheet(
builder: (BuildContext context, ScrollController scrollController) {
return Container(
color: Colors.red,
child: ListView.builder(
itemCount: 2,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('text'));
},
),
);
},
),
),
);
How can I make the contents of the sheet to start at the top? I have tried enclosing the listview with Align class and set the alignment to topcenter but same result.

You should remove your SizedBox and pass in expand: false to fix your immediate problem.
In addition, if you want a ListView to scroll with the DraggableScrollableSheet, you need to set isScrollControlled: true, and pass the controller to ListView as well.
Here is a full example of using a ListView as child:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyHome()));
}
class MyHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('BottomSheet Demo')),
body: Center(
child: ElevatedButton(
child: Text('Show DraggableScrollableSheet'),
onPressed: () async {
final result = await showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (_) => DraggableScrollableSheet(
expand: false,
builder: (context, ScrollController controller) {
return ListView.builder(
controller: controller,
itemCount: 100,
itemExtent: 50,
itemBuilder: (context, index) {
return Center(child: Text('Item $index'));
},
);
},
),
);
print(result);
},
),
),
);
}
}

DraggableScrollableSheet(initialChildSize: 1.0,
builder: (BuildContext context, ScrollController scrollController) {

Related

Navigator operation requested with a context that does not include a Navigator. (Builder function wont display MainPage())

As soon as I press the button I get the error code "Navigator operation requested with a context that does not include a Navigator." Code im using to navigate is in the RegisterPage and by the builder function should be displaying the MainPage.
void main() => runApp(RegisterPage());
class RegisterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Register Your Account'),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.of(context)
.push(
MaterialPageRoute(
builder: (context) => MainPage()
),
);
},
child: Text('Sign in as guest'),
)),
)
);
}
}
class MainPage extends StatefulWidget{
String get title => "Cykla i stockholm";
MapPage createState()=> MapPage();
}
That's because the widget which uses the navigator (RegisterPage) is at the same level in the widget tree with the widget which creates the navigator(MaterialApp)
SOLUTION:
make the RegisterPage below MaterialApp in order to be able to use its context:
class RegisterPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Register Your Account'),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Builder(
builder: (ctx)=> Center(//this context here has access to Navigator
child: RaisedButton(
onPressed: () {
Navigator.of(ctx)
.push(
MaterialPageRoute(
builder: (context) => MainPage()
),
);
},
child: Text('Sign in as guest'),
)),
),
)
);
}
}

Flutter listview not updating after data update

I have a ListView inside a bottomSheet, that is built using an array of elements. Currently I have one item in there "empty" which is then .clear()ed and populated after an async DB call.
The variable update is correct, and I try to use setState((){}) but the ListView isn't updated at all. I need to close the bottomSheet, reopen it, and the ListView then has the correct items.
Do I need to just call setState or does the bottomSheet builder need to be flagged to update?
Main ListView section:
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My Map'),
backgroundColor: Colors.green[700],
),
//Put in a stack widget so can layer other widgets on top of map widget
body: Stack(
children: <Widget>[
GoogleMap(
mapType: _currentMapType,
markers: _markers,
onMapCreated: _onMapCreated,
onCameraMove: _onCameraMove,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
alignment: Alignment.bottomCenter,
child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
SizedBox(width: 16.0),
Builder(
builder: (context) => FloatingActionButton(
...
),
),
SizedBox(width: 16.0),
FloatingActionButton(
...
),
SizedBox(width: 16.0),
Builder(
builder: (context) => FloatingActionButton(
child: Icon(Icons.file_download, size: 36.0),
backgroundColor: Colors.green,
onPressed: () {
showBottomSheet(
context: context,
builder: (context) {
return ListView(
padding: EdgeInsets.all(15.0),
children: <Widget>[
...
Divider(),
ListTile(
title: Text("Remote JSON Download"),
trailing:
Icon(Icons.refresh),
selected: true,
onTap: _OnPressedReloadJSON, <-------------
),
Divider(),
Container(
height: 150.0,
child: ListView.builder( <-------------
scrollDirection: Axis.horizontal,
itemCount : testList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
padding: EdgeInsets.all(5.0),
margin: EdgeInsets.all(5.0),
width: 150.0,
color: Colors.red,
child: Text(testList[index]),
);
},
),
),
Async Get:
class _MyAppState extends State<MyApp> {
...
_OnPressedReloadJSON() {
fetchJSONAndMakeListTile();
}
...
List<String> testList= ["empty"];
Future<http.Response> fetchJSONAndMakeListTile() async {
final response = await http.get('https://..... file.json');
// If the server did return a 200 OK response, then parse the JSON.
if (response.statusCode == 200) {
List<db_manager.MyObject> myObjects = (json.decode(response.body) as List)
.map((e) => db_manager.MyObject.fromJson(e))
.toList();
testList.clear();
myObjects.forEach((db_manager.MyObject al) {
testList.add(al.code);
print("debug:"+al.code+" - "+al.name); <------------- prints correctly
});
//TODO Does this even work?
//Trigger update of state - ie redraw/reload UI elements
setState(() {});
} else {
// If the server did not return a 200 OK response, then throw an exception.
print(response);
throw Exception('Failed to load json');
}
}
UPDATE:
I've abstracted the BottomSheet builder into another class (as per another answer) as its own StatefulWidget but I can't seem to access the void onPress() method from my main dart file. If the BottomSheet creation/builder is in this separate dart file, how do I call it to build and then update its state with the async call updating the listview contents List?
BottomSheetWidget.dart
class BottomSheetDatabases extends StatefulWidget {
#override
_BottomSheetDatabases createState() => _BottomSheetDatabases();
}
class _BottomSheetDatabases extends State<BottomSheetDatabases> {
void _onpress() {
}
void loadMe() {
}
List<String> testList= ["empty"];
#override
Widget build(BuildContext context) {
return BottomSheet(
builder: (context) {
return ListView(
padding: EdgeInsets.all(15.0),
children: <Widget>[
ListTile(
...
),
Divider(),
Container(
height: 150.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: testList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
key: UniqueKey(),
//TODO ??
padding: EdgeInsets.all(5.0),
margin: EdgeInsets.all(5.0),
width: 150.0,
color: Colors.red,
child: Text(testList[index]),
);
},
),
//),
//),
),
...
Main.dart:
void _loadSheetDatabases() {
BottomSheetWidget bottomSheetwidget = BottomSheetWidget();
bottomSheetwidget.loadMe();
}
Seems to me that a Key are missing into widget that ListView.Builder returns, try to put a UniqueKey() or ValueKey into Container or Text:
Container(
key: UniqueKey(),
padding: EdgeInsets.all(5.0),
margin: EdgeInsets.all(5.0),
width: 150.0,
color: Colors.red,
child: Text(testList[index]),
);
Your ListView is only built when onPressed is called on your FloatingActionButton.
I assume that's why it doesn't get built again when the state changes.
You could wrap the code to build the ListView in a new StatefulWidget with its own state and then update it when testList changes.

Flutter Navigator.pop won't send data back

This is my code so far. I dont know whats wrong, I tried a lots of youtube turolial and other things as well, but its looks like the pop wont give back the correct data. I really need help, i spend 2 days already
void main() {
List<String> names = [];
List<String> mgs = [];
runApp(MaterialApp(
title: 'Returning Data',
home: HomeScreen(names, mgs),
));
}
class HomeScreen extends StatelessWidget {
List<String> names = [];
List<String> mgs = [];
HomeScreen(this.names, this.mgs);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text("Project_Drink"),
),
body: Container(
child: Column(
children: <Widget>[
new Expanded(
child: ListView.builder
(
itemCount: mgs.length,
itemBuilder: (context, Index) {
return Text("Name: " + names[Index]
+" "+ "Mg: " + mgs[Index]);
}
)
)
],
),
),
bottomNavigationBar :BottomAppBar (
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
const Expanded(child: Text("TOTAL : 200")),
FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddProduct()),
);
},
child: Icon(Icons.add),
),
],
),
),
),
);
}
}
This is the AddProduct i want this to send back the data and then i should be able to put in into a list.Lika a not pad
class AddProduct extends StatefulWidget {
#override
State createState() => new _AddProductState();
}
class _AddProductState extends State<AddProduct> {
List<String> names = [];
List<String> mgs = [];
//final TextEditingController eCtrl = new TextEditingController();
final nameController = TextEditingController();
final mgController = TextEditingController();
#override
Widget build (BuildContext ctxt) {
return new Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text("New Drink"),
),
body: new Column(
children: <Widget>[
new TextField(
decoration: InputDecoration(
hintText: "Name",
),
controller: nameController,
),
new TextField(
decoration: InputDecoration(
hintText: "Mg",
suffixText: "Mg",
),
controller: mgController,
),
RaisedButton(
onPressed: (){
names.add(nameController.text);
mgs.add(mgController.text);
setState(() {});
nameController.clear();
mgController.clear();
Navigator.pop(context, names + mgs);
},
child: Text("ADD"),
),
],
)
);
}
}
I moved your lists to HomeScreen widget instead of main function. Your main function should just run the app
void main() {
runApp(
MaterialApp(
title: 'Returning Data',
home: HomeScreen(),
),
);
}
And I convert your HomeScreen widget to StatefulWidget insted of StatelessWidget because when you add new items and display it screen your state will change and StatelessWidget is not able to do that. It will be something like that
Navigator.push returns a future value so if you want to declare a variable with the data comes from that, you need to await for it. After you get the data you can add them into your lists but it needs to be inside setState function to update UI
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<String> names = [];
List<String> mgs = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text("Project_Drink"),
),
body: Container(
child: Column(
children: <Widget>[
new Expanded(
child: ListView.builder(
itemCount: mgs.length,
itemBuilder: (context, index) {
return Text("Name: " + names[index] + " " + "Mg: " + mgs[index]);
}
),
),
],
),
),
bottomNavigationBar: BottomAppBar(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Expanded(child: Text("TOTAL : 200")),
FloatingActionButton(
onPressed: () async {
// Navigator.push returns a future value so you need to await for it
var data = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddProduct()),
);
// After you get the data from the other page you need to add them into your lists inside setState function to update UI
setState(() {
names.add(data[0]);
mgs.add(data[1]);
});
},
child: Icon(Icons.add),
),
],
),
),
),
);
}
}
I didn't change anything in your AddProduct widget.
class AddProduct extends StatefulWidget {
#override
State createState() => new _AddProductState();
}
class _AddProductState extends State<AddProduct> {
List<String> names = [];
List<String> mgs = [];
//final TextEditingController eCtrl = new TextEditingController();
final nameController = TextEditingController();
final mgController = TextEditingController();
#override
Widget build (BuildContext ctxt) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text("New Drink"),
),
body: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: "Name",
),
controller: nameController,
),
TextField(
decoration: InputDecoration(
hintText: "Mg",
suffixText: "Mg",
),
controller: mgController,
),
RaisedButton(
onPressed: (){
names.add(nameController.text);
mgs.add(mgController.text);
setState(() {});
nameController.clear();
mgController.clear();
Navigator.pop(context, names + mgs);
},
child: Text("ADD"),
),
],
)
);
}
}
Although this code should work as you want, I would suggest you to have a look at some State Management methods such as Provider, Bloc and etc. It will be more effective to create what you want to do.
try this one with simple way , when press then call this method
goToView() async {
bool data=await Navigator.push(context, new CupertinoPageRoute(builder: (BuildContext context) {
return new CoolForgot();
}));
print(data);
}
My Next View is Forgot Screen so i have used CoolForgot(), you can use as per your requirement.
then when press on back button in Next View(In my case CoolForgot()) called this
Navigator.pop(context,true);
I have pass bool value and get bool value , you can pass any type of object and get from it.

How do I make it so that all filters don't change color when tapped on flutter

I'm trying to make a template for a filter that takes in one parameter (the tag name) and gets highlighted when tapped. But the problem with this is when one filter is tapped all of them change color because they all use the same boolean value. Sorry, I'm a beginner and I think I'm going about this the wrong way
class _HomeState extends State<Home> {
bool filterTap = true;
GestureDetector filterTemplate(String tag) {
return GestureDetector(
onTap: () {
setState(() {
filterTap = !filterTap;
});
},
child: Center(
child: Container(
margin: const EdgeInsets.only(right: 20.0),
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
color: filterTap ? Colors.grey : Colors.transparent,
),
child: Text(
tag,
style: TextStyle(
color: filterTap ? Colors.grey[900] : Colors.grey,
letterSpacing: 2.0,
),
),
),
),
);
}
first of all define a StructFilter class with its properties, For example here is an option:
class StructFilter {
StructFilter(this.tag,this.filterTap);
String tag;
bool filterTap;
}
Then collect all of your filter information into a list of StructFilter(i.e List<StructFilter> filterList).
For example you can try:
Listview(
children: filterList.map((item){
return filterTemplate(item);
}).toList();
)
GestureDetector filterTemplate(StructFilter structFilter) {
return GestureDetector(
onTap: () {
setState(() {
structFilter.filterTap = !structFilter.filterTap;
});
},
),
);
}
Use List or Map or List<YourClass> to maintain status of each button.
And try ChoiceChip,
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Home()));
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
Map<String, bool> tagsList = {
"Tag1": false,
"Tag2": false,
"Tag3": false,
"Tag4": false,
};
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: tagsList.entries.map((entry) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ChoiceChip(
label: Text(entry.key),
selected: entry.value,
onSelected: (value) {
setState(() {
tagsList[entry.key] = value;
});
},
),
);
}).toList(),
),
),
);
}
}

How can I implement search in this Flutter app

I'm trying to add a search feature to this flutter app since the json file it pulls data from has 7000 results.
Mainly I'm trying to do search for "ctry" and "peopnameincountry". This was ripped from https://www.youtube.com/watch?v=EwHMSxSWIvQ
As is .. it works fine in fetching the json list and the tap to show detail page works as well.
I just need to implement the search on the main page so I don't have to scroll through the thousands of results.
Appreciate any help .. thank you all.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(new UnReached());
class UnReached extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Unreached'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<List<User>> _getUsers() async {
var data = await http.get("https://cmfiflutterapp.s3-ap-southeast-2.amazonaws.com/UnreachedPeoplesGroup.json");
var jsonData = json.decode(data.body);
List<User> users = [];
for(var u in jsonData){
User user = User(u["ctry"], u["peopnameincountry"], u["population"], u["primarylanguagename"], u["biblestatus"], u["primaryreligion"], u["continent"]);
users.add(user);
}
print(users.length);
return users;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text(widget.title),
),
body: Container(
child: FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot){
print(snapshot.data);
if(snapshot.data == null){
return Container(
child: Center(
child: Text("Loading...")
)
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: Icon(Icons.arrow_forward_ios),
// leading: CircleAvatar(
// backgroundImage: NetworkImage(
// snapshot.data[index].picture
// ),
// ),
title: Text(snapshot.data[index].peopnameincountry),
subtitle: Text(snapshot.data[index].ctry),
onTap: (){
Navigator.push(context,
new MaterialPageRoute(builder: (context) => DetailPage(snapshot.data[index]))
);
},
);
},
);
}
},
),
),
);
}
}
Try adding these function in your code:
import 'package:flutter/material.dart';
import 'dart:core';
class HomeScreen1 extends StatefulWidget {
#override
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen1> {
var searchController = new TextEditingController();
String search;
List<String> _filterList;
String _query = "";
bool _firstSearch = true;
#override
void initState() {
super.initState();
}
HomeScreenState() {
searchController.addListener(() {
if (searchController.text.isEmpty) {
setState(() {
_firstSearch = true;
_query = "";
});
} else {
setState(() {
_firstSearch = false;
_query = searchController.text;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: new Container(
margin: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0),
child: new Column(
children: <Widget>[
_createSearchView(),
new Expanded(
child: _firstSearch ? _createListView() : _performSearch(),
),
],
),
),
);
}
Widget _createSearchView() {
return new Container(
decoration: BoxDecoration(border: Border.all(width: 1.0)),
child: new TextField(
controller: searchController,
decoration: InputDecoration(
icon: Icon(Icons.search),
hintText: "Search",
hintStyle: new TextStyle(color: Colors.grey[300]),
),
//textAlign: TextAlign.center,
),
);
}
Widget _createListView() {
return FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot){
print(snapshot.data);
if(snapshot.data == null){
return Container(
child: Center(
child: Text("Loading...")
)
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: Icon(Icons.arrow_forward_ios),
// leading: CircleAvatar(
// backgroundImage: NetworkImage(
// snapshot.data[index].picture
// ),
// ),
title: Text(snapshot.data[index].peopnameincountry),
subtitle: Text(snapshot.data[index].ctry),
onTap: (){
Navigator.push(context,
new MaterialPageRoute(builder: (context) => DetailPage(snapshot.data[index]))
);
},
);
},
);
}
},
),
}
Widget _performSearch() {
return FutureBuilder<List>(builder: (context, snapshot) {
_filterList = new List<String>();
for (int i = 0; i < snapshot.data.length; i++) {
var item = snapshot.data[i];
if ((item.toString().toLowerCase()).contains(_query.toLowerCase())) {
_filterList.add(item.toString());
}
}
return _createFilteredListView();
});
}
Widget _createFilteredListView() {
return ListView.builder(
itemCount: _filterList.length,
itemBuilder: (BuildContext context, int index) {
return new Card(
color: Colors.white,
elevation: 5.0,
child: new Container(
margin: EdgeInsets.all(15.0),
child: new Text("${_filterList[index]}"),
),
);
});
}
}
The concept of a FutureBuilder widget is to be build as soon as it is received, but meanwhile, the snapshot contains no data at all. So when you're calling :
for (int i = 0; i < snapshot.data.length; i++) {
you're, at least at first, calling length on null since the data is not yet received.
The solution is to create a switch and call `snapshot.data when the status is completed:
switch (snapshot.connectionState) {
case ConnectionState.none:
return DefaultWidget(); // For instance a CircularProgress
case ConnectionState.active:
return DefaultWidget(); // For instance a CircularProgress
case ConnectionState.waiting:
return DefaultWidget(); // For instance a CircularProgress
case ConnectionState.done:
if (snapshot.hasError)
return ErrorWidget('Error: ${snapshot.error}'); //For example a Text Widget
// Your code here:
_filterList = new List<String>();
for (int i = 0; i < snapshot.data.length; i++) {
var item = snapshot.data[i];
if ((item.toString().toLowerCase()).contains(_query.toLowerCase())) {
_filterList.add(item.toString());
}
}
return _createFilteredListView();
}
return null; // unreachable
More on this here
My apologies guys...I ended up taking a slightly different approach which I thought was slightly faster in response than the FutureBuilder approach. Maybe it's just my internet. Not sure.
import 'package:flutter/material.dart';
import 'dart:core';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'package:progress_indicators/progress_indicators.dart';
class IslandWaves extends StatefulWidget {
#override
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<IslandWaves> {
List<User> _list = [];
List<User> _search = [];
var loading = false;
Future<Null> fetchData() async {
setState(() {
loading = true;
});
_list.clear();
final response = await http.get(
"https://cmfiflutterapp.s3-ap-southeast-2.amazonaws.com/UnreachedPeoplesGroup.json");
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
setState(() {
for (Map i in data) {
_list.add(User.formJson(i));
loading = false;
}
});
}
}
TextEditingController controller = new TextEditingController();
onSearch(String text) async {
_search.clear();
if (text.isEmpty) {
setState(() {});
return;
}
_list.forEach((f) {
if (f.ctry.contains(text) ||
f.peopnameincountry.toString().contains(text)) _search.add(f);
});
setState(() {});
}
#override
void initState() {
// TODO: implement initState
super.initState();
fetchData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10.0),
color: Colors.blue,
child: Card(
child: ListTile(
leading: Icon(Icons.search),
title: TextField(
controller: controller,
onChanged: onSearch,
decoration: InputDecoration(
hintText: "Search", border: InputBorder.none),
),
trailing: IconButton(
onPressed: () {
controller.clear();
onSearch('');
},
icon: Icon(Icons.cancel),
),
),
),
),
loading
? Center(
heightFactor: 20.0,
child: FadingText('Loading...'),
)
: Expanded(
child: _search.length != 0 || controller.text.isNotEmpty
? ListView.builder(
itemCount: _search.length,
itemBuilder: (context, i) {
final b = _search[i];
return GestureDetector(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) =>
DetailPage(_search[i])));
debugPrint('TopNav');
},
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
b.ctry,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0),
),
SizedBox(
height: 4.0,
),
Text(b.peopnameincountry),
],
)),
);
},
)
: ListView.builder(
itemCount: _list.length,
itemBuilder: (context, i) {
final a = _list[i];
return GestureDetector(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) =>
DetailPage(_list[i])));
debugPrint('BottomNav');
},
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
a.ctry,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0),
),
SizedBox(
height: 4.0,
),
Text(a.peopnameincountry),
],
)
),
);
},
),
),
],
),
),
);
}
}
class DetailPage extends StatelessWidget{....etc.}

Resources