opening page multiple times bug - dart

future builder running multiple times and page opening multiple at the same time. How to fix this error?
SubCategory StatefulWidget
class subCategory extends StatefulWidget {
final int RegId;
final int AssetId;
final String title;
final int ParentId;
final int equipmentId;
subCategory(this.RegId, this.AssetId, this.title, this.ParentId,this.equipmentId, {Key key})
: super(key: key);
#override
State<StatefulWidget> createState() => _mainCategory();
}
_mainCategory State Widget
class _mainCategory extends State<subCategory> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: submitRequestAppBar(context),
body: Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey[350],
leading: Container(),
title: Text(
widget.title,
textAlign: TextAlign.left,
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black),),),
FutureBuilder inside Body
body: FutureBuilder(
future: getRegister1(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return waitingCircle();
case ConnectionState.done:
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
if (snapshot.data.length == 0)
return noResultFound();
else
return createListView(context, snapshot); }
return null; // unreachable
},)),); }
CreateListView Widget
Widget createListView(BuildContext context, AsyncSnapshot snapshot) {
List<SubCategoryItem> values = snapshot.data;
return ListView
return ListView.builder(
padding: EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return GridView.count(
physics: ScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 4,
children: List.generate(values.length, (index) {
return GridTile
return GridTile(
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => WoDescription2(
widget.RegId,
widget.AssetId,
widget.ParentId,
values[index].childId,
false,
1,
widget.title,
widget.equipmentId))),
Column Widget
child: Column(
children: [
Card(
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent, width: 1.5)),
child: Stack(
children: <Widget>[
flutter_svg plugin use for svg images
SvgPicture.asset('assets/images/Defect/icon-${values[index].childId}.svg',
height: 50.0,),],),), ),
Expanded(
child: Text(
'${values[index].description}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 10.0),),),],),), ); }),); },);}
FutureBuider future:getRegister1
Future getRegister1() async {
List<SubCategoryItem> description = [];
List cat = [];
var catLocal = (await HelperDatabase1().displayDefCatRelation());
var defCatLocal = (await HelperDatabase1().display());
for (int i = 0; i < catLocal.length; i++) {
if (widget.RegId == catLocal[i].r &&
widget.AssetId == catLocal[i].t &&
widget.ParentId == catLocal[i].p) {
cat.add(catLocal[i].c);}}
for (int i = 0; i < cat.length; i++) {
for (int j = 0; j < defCatLocal.length; j++) {
if (cat[i] == defCatLocal[j].deF_CAT_ID) {
var oneItem = SubCategoryItem(
childId: defCatLocal[j].deF_CAT_ID,
description: defCatLocal[j].description);
await description.add(oneItem);}}}
return description;}}
SubCategoryItem class
class SubCategoryItem {
int childId;String description;
SubCategoryItem({this.childId, this.description});}

Just declare the Future out de Build method / on the initState
_mainCategory State Widget
class _mainCategory extends State<subCategory> {
Future _futureData;
#override
void initState() {
super.initState();
_futureData = getRegister1();
}
#override
Widget build(BuildContext context) {
return Scaffold(
FutureBuilder inside Body
body: FutureBuilder(
future: _futureData,
builder: (BuildContext context, AsyncSnapshot snapshot) {

Related

Display icons on a flutter app from cloud firestore

I am currently displaying my icons like this:
Widget _buildPopupDialog(BuildContext context) {
List<IconData> _iconsTable = [
Icons.feedback,
Icons.eco,
Icons.support,
Icons.call,
Icons.nature_people,
Icons.directions_bike,
];
return new AlertDialog(
content: SingleChildScrollView(
child: new Container(
child: GridView.count(
children: new List.generate(6, (int index) {
return new Positioned(
child: new DailyButton(iconData: _iconsTable[index]),
);
}),
),
),
),
However, I am wanting to get the icon data from cloud firestore. I am very new to using both flutter and firebase so I am very unsure how I would be able to do this. So far, I have tried this but iconData: Icons.iconsData obviously doesnt work:
class MyApp3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage3(),
);
}
}
class MyHomePage3 extends StatefulWidget {
#override
_MyHomePageState3 createState() {
return _MyHomePageState3();
}
}
class _MyHomePageState3 extends State<MyHomePage3> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('icons').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.docs);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
var _iconsData = record3.name;
return Padding(
key: ValueKey(record3.name),
child: Container(
child: Card(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: Icons.iconsData,
),
// trailing: Text(record3.votes.toString()),
// onTap: () => record3.reference.update({'votes': record3.votes+1})
),
),
);
}
}
class Record3 {
final String name;
final int votes;
final DocumentReference reference;
Record3.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
assert(map['votes'] != null),
name = map['name'],
votes = map['votes'];
Record3.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data(), reference: snapshot.reference);
#override
String toString() => "Record<$name:$votes>";
}
Any help would be greatly appreciated!
If anyone is interested, I was able to figure it out:
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
int iconCode = record3.votes;
return Padding(
key: ValueKey(record3.name),
child: Container(
child: new Container(
child: new ListView(
scrollDirection: Axis.horizontal,
children: new List.generate(1, (int index) {
return new Positioned(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: (IconData(iconCode, fontFamily: 'MaterialIcons')),
),
);
})),
),
),
);

flutter , I Want Change Qty List From StreamController?

flutter , I Want Change Qty List From StreamController ?
I want action ontap
IconButton Change data
Text(poduct[index].qty.toString()),
from StreamController
I don't want to use setState(() {});
import 'package:flutter/material.dart';
import 'dart:async';
void main() {
runApp(new MaterialApp(title: "Simple Material App", home: new MyHome()));
}
class MyHome extends StatefulWidget {
#override
MyHomeState createState() => new MyHomeState();
}
class Product {
String productName;
int qty;
Product({this.productName, this.qty});
}
class MyHomeState extends State<MyHome> {
List<Product> poduct = [Product(productName: "Nike",qty: 20),Product(productName: "Vans",qty: 30),];
var listPoduct = StreamController<List<Product>>();
#override
void initState() {
listPoduct.sink.add(poduct);
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("test stream"),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: StreamBuilder(
stream: listPoduct.stream,
builder: (context, snapshot) {
return ListView.builder(
itemCount: poduct.length,
padding: EdgeInsets.all(10),
itemBuilder: (BuildContext context, int index){
return Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(poduct[index].productName,style: TextStyle(fontSize: 24.0),),
new IconButton(icon: new Icon(Icons.remove), onPressed: (){
// How to Add ? listPoduct.sink ?
}),
Text(poduct[index].qty.toString()), /// <<< I Want Change Qty List Form StreamController
new IconButton(icon: new Icon(Icons.add), onPressed: (){
// How to Add ? listPoduct.sink ?
}),
Divider(),
],
),
);
},
);
}
),
));
}
}
I want action ontap
IconButton Change data
Text(poduct[index].qty.toString()),
from StreamController
I don't want to use setState(() {});
void main() {
runApp(new MaterialApp(title: "Simple Material App", home: new MyHome()));
}
class MyHome extends StatefulWidget {
#override
MyHomeState createState() => new MyHomeState();
}
class Product {
String productName;
int qty;
Product({this.productName, this.qty});
}
class MyHomeState extends State<MyHome> {
List<Product> poduct = [ // <<<<<<<< TYPO HERE
Product(productName: "Nike",qty: 20),
Product(productName: "Vans",qty: 30)];
var listPoduct = StreamController<List<Product>>();
#override
void initState() {
listPoduct.sink.add(poduct);
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("test stream"),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: StreamBuilder(
stream: listPoduct.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length, // <<<<<<<< . note that listbuilder relies on snapshot not on your poduct property
padding: EdgeInsets.all(10),
itemBuilder: (BuildContext context, int index){
return Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(poduct[index].productName,style: TextStyle(fontSize: 24.0),), // <<<<<<<< you can also use here the snapshot.data
new IconButton(icon: new Icon(Icons.remove), onPressed: () {
_update(index, -1);
}),
Text(poduct[index].qty.toString()), // <<<<<<<< you can also use here the snapshot.data
new IconButton(icon: new Icon(Icons.add), onPressed: (){
_update(index, 1);
}),
Divider(),
],
),
);
},
);
} else {
return Container()
}
}
),
));
}
_update(int index, int difference) {
for (int i = 0; i < poduct.length; i++ ) {
if (i == index) {
poduct[i] =
Product(productName: poduct[i].productName,
qty: poduct[i].qty + difference);
}
}
listPoduct.add(poduct);
}
}
some helpful links:
StreamBuilder-class
Example

SliverAppbar remains visible when CustomScrollView with ScrollController is scrolled

Adding a ScrollController to function timelineList() causes the SliverAppBar to remain visible on scroll (when counter list is scrolled, SliverAppBar should hide). The issue goes away if the _scrollController is removed from the list (see timelineList function) but this gives rise to a new problem, I need to listen for when the scrollbar reaches bottom (to get more content).
See sample app below, copy/paste and run.
void main() => runApp(TestApp());
class TestApp extends StatelessWidget {
final _scrollController = new ScrollController();
TestApp(){
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print('Get more data');
}
});
}
#override
Widget build(BuildContext context) {
TestBloc bloc = TestBloc();
bloc.fetchTestTimeline();
bloc.fetchTestAppBarTxt1();
bloc.fetchTestAppBarTxt2();
return MaterialApp(
home: new Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
backgroundColor: Colors.blueGrey,
elevation: 0.0,
),
body: NestedScrollView(
headerSliverBuilder:
(BuildContext contrxt, bool innerBoxIsScrolled) {
return <Widget>[
buildSliverAppBar(context, bloc),
];
},
body: Column(
children: <Widget>[
timelineList(bloc),
],
)
)),
);
}
buildSliverAppBar(context, TestBloc bloc){
return SliverAppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.grey[400],
expandedHeight: 200.0,
floating: true,
snap: true,
flexibleSpace: FlexibleSpaceBar(
background: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 2.0),
height: 200,
child: Column(
children: <Widget>[
StreamBuilder(
stream: bloc.testAppBarTxt1,
initialData: null,
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
if (snapshot.data == null)
return buildProgressIndicator(true);
return Expanded(
child: Text('${snapshot.data}'));
}),
StreamBuilder(
stream: bloc.testAppBarTxt2,
initialData: null,
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
if (snapshot.data == null)
return buildProgressIndicator(true);
return Expanded(
child: Text('${snapshot.data}'));
}),
],
),
)
],
),
));
}
timelineList(TestBloc bloc) {
return StreamBuilder(
stream: bloc.getTestTimeline,
initialData: null,
builder: (BuildContext context, AsyncSnapshot<List<int>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Expanded(child: buildProgressIndicator(true));
}
List<int> val = snapshot.data;
if (val.isNotEmpty) {
addToTimelineList(val, bloc);
return Expanded(
child: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(new List<Widget>.generate(bloc.listTest.length, (int index) {
if (index == bloc.listTest.length) {
return buildProgressIndicator(bloc.isPerformingRequest);
} else {
return bloc.listTest[index];
}
})
))
],
),
);
}
});
}
void addToTimelineList(List<int> list, TestBloc bloc) {
for (var val in list) {
bloc.listTest.add(Text('$val'));
}
}
}
Widget buildProgressIndicator(showIndicator) {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: showIndicator ? 1.0 : 0.0,
child: Container(
width: 10.0,
height: 10.0,
child: new CircularProgressIndicator(
)),
),
),
);
}
class TestBloc {
String appbar1Val;
String appbar2Val;
List<Text> listTest = new List<Text>();
bool isPerformingRequest = false;
final _testAppBarText1 = BehaviorSubject<String>();
Observable<String> get testAppBarTxt1 => _testAppBarText1.stream;
final _testAppBarText2 = BehaviorSubject<String>();
Observable<String> get testAppBarTxt2 => _testAppBarText2.stream;
final _testTimeline = PublishSubject<List<int>>();
Observable<List<int>> get getTestTimeline => _testTimeline.stream;
fetchTestTimeline() async {
List item = await Future.delayed(
Duration(seconds: 2), () => List<int>.generate(100, (i) => i));
_testTimeline.sink.add(item);
}
fetchTestAppBarTxt1() async {
appbar1Val = await Future.delayed(Duration(seconds: 2), () => "Text One");
_testAppBarText1.sink.add(appbar1Val);
}
fetchTestAppBarTxt2() async {
appbar2Val = await Future.delayed(Duration(seconds: 2), () => "Text Two");
_testAppBarText2.sink.add(appbar2Val);
}
dispose() {
_testAppBarText1.close();
_testAppBarText2.close();
_testTimeline.close();
}
}
It's possible to achive the same result with wrapping your list with a Notification Listener.
NotificationListener<ScrollNotification>(
onNotification: (sn) {
if (sn.metrics.pixels ==
sn.metrics.maxScrollExtent) {
print('Get more data');
}
},
child: CustomScrollView(...
Edit: Since my initial answer didn't cover the animateTo use case, I got it working by removing the outer NestedScrollView. Here is the modified example.
void main() => runApp(TestApp());
class TestApp extends StatelessWidget {
final _scrollController = new ScrollController();
TestApp() {
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
print('Get more data');
}
});
}
#override
Widget build(BuildContext context) {
TestBloc bloc = TestBloc();
bloc.fetchTestTimeline();
bloc.fetchTestAppBarTxt1();
bloc.fetchTestAppBarTxt2();
return MaterialApp(
home: new Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
backgroundColor: Colors.blueGrey,
elevation: 0.0,
),
body: Column(
children: <Widget>[
timelineList(bloc),
],
),
),
);
}
buildSliverAppBar(context, TestBloc bloc) {
return SliverAppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.grey[400],
expandedHeight: 200.0,
floating: true,
snap: true,
flexibleSpace: FlexibleSpaceBar(
background: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 2.0),
height: 200,
child: Column(
children: <Widget>[
StreamBuilder(
stream: bloc.testAppBarTxt1,
initialData: null,
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
if (snapshot.data == null)
return buildProgressIndicator(true);
return Expanded(child: Text('${snapshot.data}'));
}),
StreamBuilder(
stream: bloc.testAppBarTxt2,
initialData: null,
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
if (snapshot.data == null)
return buildProgressIndicator(true);
return Expanded(child: Text('${snapshot.data}'));
}),
],
),
)
],
),
));
}
timelineList(TestBloc bloc) {
return StreamBuilder(
stream: bloc.getTestTimeline,
initialData: null,
builder: (BuildContext context, AsyncSnapshot<List<int>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Expanded(child: buildProgressIndicator(true));
}
List<int> val = snapshot.data;
if (val.isNotEmpty) {
addToTimelineList(val, bloc);
return Expanded(
child: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
buildSliverAppBar(context, bloc),
SliverList(
delegate: SliverChildListDelegate(
new List<Widget>.generate(bloc.listTest.length,
(int index) {
if (index == bloc.listTest.length) {
return buildProgressIndicator(bloc.isPerformingRequest);
} else {
return bloc.listTest[index];
}
})))
],
),
);
}
});
}
void addToTimelineList(List<int> list, TestBloc bloc) {
for (var val in list) {
bloc.listTest.add(Text('$val'));
}
}
}
Widget buildProgressIndicator(showIndicator) {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: showIndicator ? 1.0 : 0.0,
child: Container(
width: 10.0, height: 10.0, child: new CircularProgressIndicator()),
),
),
);
}
class TestBloc {
String appbar1Val;
String appbar2Val;
List<Text> listTest = new List<Text>();
bool isPerformingRequest = false;
final _testAppBarText1 = BehaviorSubject<String>();
Observable<String> get testAppBarTxt1 => _testAppBarText1.stream;
final _testAppBarText2 = BehaviorSubject<String>();
Observable<String> get testAppBarTxt2 => _testAppBarText2.stream;
final _testTimeline = PublishSubject<List<int>>();
Observable<List<int>> get getTestTimeline => _testTimeline.stream;
fetchTestTimeline() async {
List item = await Future.delayed(
Duration(seconds: 2), () => List<int>.generate(100, (i) => i));
_testTimeline.sink.add(item);
}
fetchTestAppBarTxt1() async {
appbar1Val = await Future.delayed(Duration(seconds: 2), () => "Text One");
_testAppBarText1.sink.add(appbar1Val);
}
fetchTestAppBarTxt2() async {
appbar2Val = await Future.delayed(Duration(seconds: 2), () => "Text Two");
_testAppBarText2.sink.add(appbar2Val);
}
dispose() {
_testAppBarText1.close();
_testAppBarText2.close();
_testTimeline.close();
}
}

Perform Async Operations from Data gotten from StreamBuilder before displaying

I am currently using StreamBuilder to get data from Firestore and so far, it is working good.
I currently want to perform some async operations to the data before displaying.
The code to get the data is below.
List<Model> listToDisplay = new List<Model>();
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: topBar,
body: StreamBuilder(
stream: Firestore.instance.collection('/myPath').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if(snapshot.connectionState == ConnectionState.active) {
listToDisplay.clear();
for (DocumentSnapshot _snap in snapshot.data.documents) {
Model _add = new Model.from(_snap);
listToDisplay.add(_add);
}
return TabBarView(
children: <Widget>[
ListView.builder(
itemCount: mouveList.length,
itemBuilder: (context, index) {
return Card(listToDisplay[index]);
},
),
Icon(Icons.directions_transit),
],
);
} else {
return Container(
child: Center(child: CircularProgressIndicator()));
}
})));
I tried adding the async operation in the for in loop but that did not work, it did not wait for it. Also, add await did not work because Widget build(BuildContext context) cannot be async.
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: topBar,
body: StreamBuilder(
stream: Firestore.instance.collection('/myPath').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if(snapshot.connectionState == ConnectionState.active) {
listToDisplay.clear();
for (DocumentSnapshot _snap in snapshot.data.documents) {
Model _add = new Model.from(_snap);
//Added
//_add.getCalculate(); <------- Async function
_add.Calculate(); <------ Flutter does not wait for this
await _add.Calculate(); <------ Produces an error
listToDisplay.add(_add);
}
return TabBarView(
children: <Widget>[
ListView.builder(
itemCount: mouveList.length,
itemBuilder: (context, index) {
return Card(listToDisplay[index]);
},
),
Icon(Icons.directions_transit),
],
);
} else {
return Container(
child: Center(child: CircularProgressIndicator()));
}
})));
Any ideas on how to get data as stream, perform operations on the data before displaying the data all using StreamBuilder and ListViewBuilder ?
I'm currently iterating the data from a StreamBuilder in corresponding lists and then using a ListView.builder to display each data item from List.count. The code begins with these public/file Lists...
List names = new List();
List ids = new List();
List vidImages = new List();
List numbers = new List();
Then this in my Stateful Widget Builder...
child: new StreamBuilder(
stream:
fb.child('child').orderByChild('value').onValue,
builder:
(BuildContext context, AsyncSnapshot<Event> event) {
if (event.data?.snapshot?.value == null) {
return new Card(
child: new Text(
'Network Error, Please Try again...',
style: new TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic)),
);
} else if (event.data?.snapshot?.value != null) {
Map myMap =
event.data?.snapshot?.value; //store each map
var titles = myMap.values;
List onesTitles = new List();
List onesIds = new List();
List onesImages = new List();
List onesRank = new List();
List<Top> videos = new List();
for (var items in titles) {
var top = new Top(
videoId: items['vidId'],
rank: items['Value'],
title: items['vidTitle'],
imageString: items['vidImage']);
videos.add(top);
videos..sort((a, b) => b.rank.compareTo(a.rank));
}
for (var vids in videos) {
onesTitles.add(vids.title);
onesIds.add(vids.videoId);
onesImages.add(vids.imageString);
onesRank.add(vids.rank);
}
names = onesTitles;
ids = onesIds;
numbers = onesRank;
vidImages = onesImages;
switch (event.connectionState) {
case ConnectionState.waiting:
return new Card(
child: new Text('Loading...',
style: new TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic)),
);
else
return new InkWell( child: new ListView.builder(
itemCount:
names == null ? 0 : names.length,
itemBuilder:
(BuildContext context, int index) {
return new Card( child: new Text(names[index]))

Flutter ListView Item Click Listener

I have a ListView and I want to navigate to the next page on the item click.
I need an index of the clicked item of my ListView.
I know this can be done using Controller. But I couldn't find any example.
When adding the onTap for your GestureRecognizer, (or button), your closure can capture the index passed through in the itemBuilder.
E.g.
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
child: Text(index.toString()),
onTap: () => Scaffold
.of(context)
.showSnackBar(SnackBar(content: Text(index.toString()))),
);
},
itemCount: 10));
This code will display a snack bar containing the index of the ListItem that you have tapped.
Once you have the index of the item, you can navigate to a new page using code provided by other answers to this question.
If you're using a ListView.builder, you can use a ListTile to add an onTap. This will make sure you have the material ripple effect.
ListView.builder(
itemBuilder: (_, i) {
return ListTile(
title: Text('$i'),
onTap: () {}, // Handle your onTap here.
);
},
)
There's an example in the Flutter documentation that's actually this very situation (navigation to next page on item click).
As others have said, use the onTap on the item in a ListView.builder. Just thought I'd post the link to the example in case someone else needed a more full explanation.
Send data to a new screen - flutter.io
...
final List<Todo> todos;
...
ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
onTap: () {
//Go to the next screen with Navigator.push
},
);
},
);
Another alternative is to use InkWell. InkWell includes a nice ripple effect on tap, which GestureDetector does not have.
https://api.flutter.dev/flutter/material/InkWell-class.html
Use like this:
return Scaffold(
appBar: AppBar(title: Text("Hello World")),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: Text(index.toString()),
onTap: () => Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(index.toString()))),
);
},
itemCount: 10)
);
You should use the onPressed method in the item(s) you have in your ListView (or add a GestureDetector) then use Navigator, similar to the snippet below where AboutScreen is the next page you want to go to.
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AboutScreen()),
);
}
With listview in you can useing GestureDetetor();
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return new GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoomDetail()));
},
child: Container(child:(Text("This is content you want")));
ListViewpage.dart
class sta extends StatefulWidget {
const sta({Key? key}) : super(key: key);
#override
State<sta> createState() => _staState();
}
var isShow = false;
var getdata = Diohelper.getdata();
class _staState extends State<sta> {
#override
Widget build(BuildContext context) {
List mwidge = [];
int index = 0;
getdata.forEach((element) {
element.index = index;
mwidge.add(ListTile(
onTap: () {
// on click listner for move to another widget or activity (android native)
Navigator.push(context,
MaterialPageRoute(builder: (context) =>
// here element passing to the detail page element contain index other details
DetailPage(element)));
},
hoverColor: Colors.amber,
title: Text(element.name.toString()),
trailing: element.isShow
? SizedBox(
width: 100,
height: 50,
child: OutlinedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(element.name.toString())));
},
child: Text("Show")),
)
: Container(
width: 100,
),
));
index++;
});
return GestureDetector(
child: Center(
child: ListView(
children: [...mwidge],
),
),
);
}
}
DetailPage.dart
class DetailPage extends StatefulWidget {
productModel model;
DetailPage(this.model, {Key? key}) : super(key: key);
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: ListView(
children: [
Text("Name:" + widget.model.name.toString()),
Text("Category:" + widget.model.category.toString()),
Text("price:" + widget.model.price.toString())
],
),
));
}
}
FullCode:
import 'package:flutter/material.dart';
void main() =>
runApp(MaterialApp(home: Scaffold(appBar: AppBar(), body: sta())));
class sta extends StatefulWidget {
const sta({Key? key}) : super(key: key);
#override
State<sta> createState() => _staState();
}
var isShow = false;
var getdata = Diohelper.getdata();
class _staState extends State<sta> {
#override
Widget build(BuildContext context) {
List mwidge = [];
int index = 0;
getdata.forEach((element) {
element.index = index;
mwidge.add(ListTile(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailPage(element)));
},
hoverColor: Colors.amber,
title: Text(element.name.toString()),
trailing: element.isShow
? SizedBox(
width: 100,
height: 50,
child: OutlinedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(element.name.toString())));
},
child: Text("Show")),
)
: Container(
width: 100,
),
));
index++;
});
return GestureDetector(
child: Center(
child: ListView(
children: [...mwidge],
),
),
);
}
}
class DetailPage extends StatefulWidget {
productModel model;
DetailPage(this.model, {Key? key}) : super(key: key);
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: ListView(
children: [
Text("Name:" + widget.model.name.toString()),
Text("Category:" + widget.model.category.toString()),
Text("price:" + widget.model.price.toString())
],
),
));
}
}
class productModel {
String? name;
String? price;
String? category;
bool isShow = false;
int index = 0;
productModel({this.name, this.price, this.category, this.isShow = false});
productModel.fromJson(Map<String, dynamic> json) {
name = json['name'];
price = json['price'];
category = json['category'];
isShow = json['isShow'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['price'] = this.price;
data['category'] = this.category;
data['isShow'] = this.isShow;
return data;
}
}
class Diohelper {
static List<productModel> getdata() {
List<productModel> list = [];
list.add(productModel(name: "broast", price: "100", category: "chicken"));
list.add(productModel(name: "mandi", price: "100", category: "chicken"));
list.add(productModel(name: "mandi", price: "100", category: "veg"));
return list;
}
}
Dartpad or Live Code

Resources