Update Text with Dissmissble setState - flutter

I want to update my Text() value whenever I dismiss an item from the screen .
This is the MainScreen() :
Text.rich(
TextSpan(
text: total().toString() + " DT",
style: TextStyle(
fontSize: 16,
color: Colors.black,
fontWeight: FontWeight.bold),
),
The function total() is located in Product Class like this :
class Product {
final int? id;
final String? nameProd;
final String? image;
final double? price;
Product({this.id, this.nameProd, this.image, this.price});
}
List<Product> ListProduitss = [
Product(
price: 100, nameProd: 'Produit1', image: 'assets/images/freedomlogo.png')
];
double total() {
double total = 0;
for (var i = 0; i < ListProduitss.length; i++) {
total += ListProduitss[i].price!;
}
print(total);
return total;
}
I have this in the main screen .
After I remove the item from list , I want to reupdate the Text() because the function is printing a new value in console everytime I dismiss a product :
This is from statefulWidget CartItem() that I render inside MainScreen() :
ListView.builder(
itemCount: ListProduitss.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.symmetric(vertical: 10),
child: Dismissible(
key: Key(ListProduitss.toString()),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
setState(() {
ListProduitss.removeAt(index);
total();
// What to add here to update Text() value everytime
});
},
I tried to refresh the main screen but It didn't work .
onDismissed: (direction) {
setState(() {
ListProduitss.removeAt(index);
MainScreen();
});
},

One way is to declare a local string variable to use within the text. Then initialise the variable using total() within initState(). Then in setState do the same process.
However, it may be beneficial for you to look into a state management pattern such as BLoC pattern. https://bloclibrary.dev/#/
late String text;
void initState() {
super.initState();
text = Product.total();
}
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
appBar: AppBar(),
body: Column(
children: [
Text(text),
ElevatedButton(child: Text("Update"), onPressed:() => setState(() {
text = Product.total();
}),)
],
)
);
}
I am going to add another example as there was confusion to the above example. Below is an example of updated a text field with the length of the list. It is updated every time an item is removed.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
List<int> items = List<int>.generate(100, (int index) => index);
late String text;
#override
void initState() {
text = items.length.toString(); // << this is total;
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(text),
Expanded(
child: ListView.builder(
itemCount: items.length,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (BuildContext context, int index) {
return Dismissible(
background: Container(
color: Colors.green,
),
key: ValueKey<int>(items[index]),
onDismissed: (DismissDirection direction) {
setState(() {
items.removeAt(index);
text = items.length.toString(); // < this is total()
});
},
child: ListTile(
title: Text(
'Item ${items[index]}',
),
),
);
},
),
),
],
);
}
}

Related

Screen goes blank if I search for something in TextField that is out of my list

I have created a list in another directory which is a list of JSON data and I have showed it on screen and also added a TextField which works as a search bar. it works fine if I search for a letter(or combination of letters) that is inside my list but when I search for something out of list the whole page goes blank except for the Appbar.
here's my code:
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
title: 'Kindacode.com',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List _items = [];
List _itemsForDisplay = [];
// Fetch content from the json file
Future<void> readJson() async {
final String response = await rootBundle.loadString('assets/Symptoms.json');
final data = await json.decode(response);
setState(() {
_items = data["Symptoms"];
_itemsForDisplay = _items;
});
}
#override
void initState() {
// TODO: implement initState
readJson();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
'Diagnose',
),
),
body: Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
// Display the data loaded from sample.json
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
return index == 0 ? _searchBar() : _ListItem(index);
},
itemCount: _itemsForDisplay.length,
),
)
],
),
),
);
}
_searchBar() {
return Padding(
padding: const EdgeInsets.all(8),
child: TextField(
decoration: InputDecoration(hintText: 'Search'),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
_itemsForDisplay = _items.where((item) {
var itemTitle = item.toLowerCase();
return itemTitle.contains(text);
}).toList();
});
},
),
);
}
_ListItem(index) {
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
leading: Text(_itemsForDisplay[index]),
),
);
}
}
Here's a picture before for searching for something that is an entity of my list
and this happens when I search for "." or anything that is not in my list:
In your item builder return only _ListItem(index) and put _searchBar outside of Expanded. In current code, you are skipping first element from list and showing search bar instead, which is wrong. That's why when length of list is 0 you don't see anything. Because builder doesn't get executed.
So, your built method should look like this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
'Diagnose',
),
),
body: Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
// Display the data loaded from sample.json
_searchBar(),
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
return _ListItem(index);
},
itemCount: _itemsForDisplay.length,
),
)
],
),
),
);
}

'RenderBox was not laid out' when using ExpansionPanelList

I want to show a GridView on the left and a ExpansionPanelList on the right. I put them in a Row Widget but it did not work, error is :
Assertion failed:
D:\…\rendering\box.dart:1929
hasSize
"RenderBox was not laid out: RenderRepaintBoundary#bb5e9 NEEDS-LAYOUT NEEDS-PAINT"
The relevant error-causing widget was
Row
lib\main.dart:69
this is all my code in main.dart:
// ignore_for_file: prefer_const_constructors
// ignore_for_file: prefer_const_literals_to_create_immutables
import 'package:flutter/material.dart';
void main() => runApp(MyApp(UniqueKey()));
class MyApp extends StatelessWidget {
const MyApp(Key key) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(UniqueKey()),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage(Key key) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
List<int> getDataList() {
List<int> list = [];
for (int i = 0; i < 96; i++) {
list.add(i);
}
return list;
}
List<Widget> getWidgetList() {
return getDataList().map((item) => getItemContainer(item)).toList();
}
var i = 0;
Widget getItemContainer(int item) {
return Block(item);
}
//创建gridview
Widget buildGrid() {
return GridView.count(
//水平子Widget之间间距
crossAxisSpacing: 10.0,
//垂直子Widget之间间距
mainAxisSpacing: 30.0,
//GridView内边距
padding: EdgeInsets.all(10.0),
//一行的Widget数量
crossAxisCount: 4,
//子Widget宽高比例
childAspectRatio: 2.0,
//子Widget列表
children: getWidgetList(),
);
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("this is title"),
),
body: Container(
child: Row(
children: <Widget>[
Expanded(
child: buildGrid(),
),
ExpansionPanelPage(UniqueKey()),
],
)),
);
}
}
class Block extends StatelessWidget {
final int itemNo;
const Block(this.itemNo, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Listener(
onPointerHover: (event) => print(itemNo.toString()),
onPointerMove: (event) => print("aa" + itemNo.toString()),
child: Container(
alignment: Alignment.center,
child: Text(
"item",
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: Colors.blue,
),
);
}
}
// stores ExpansionPanel state information
class Item {
Item({
required this.expandedValue,
required this.headerValue,
this.isExpanded = false,
});
String expandedValue;
String headerValue;
bool isExpanded;
}
List<Item> generateItems(int numberOfItems) {
return List.generate(numberOfItems, (int index) {
return Item(
headerValue: 'Panel $index',
expandedValue: 'This is item number $index',
);
});
}
class ExpansionPanelPage extends StatefulWidget {
ExpansionPanelPage(Key key) : super(key: key);
#override
_ExpansionPanelPageState createState() => _ExpansionPanelPageState();
}
class _ExpansionPanelPageState extends State<ExpansionPanelPage> {
List<Item> _data = generateItems(1);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 10,
height: 10,
child: _buildPanel(),
),
);
}
Widget _buildPanel() {
return ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_data[index].isExpanded = !isExpanded;
});
},
children: _data.map<ExpansionPanel>((Item item) {
return ExpansionPanelRadio(
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
);
},
body: Column(
children :<Widget>[
Text("1"),
Text("1"),
Text("1"),
Text("1"),
]
),
value: item.headerValue,
);
}).toList(),
);
}
}
..........................................................................
..........................................................................
I know it might be a bit late to answer your question but for those who are facing the same issue, try wrapping ExpansionPanelList with the SingleChildScrollView widget. It helps your panel list to take all available space and to shrink-wrap in both axes. Check the docs for more info!

Flutter Scroll view to focused widget on a column

I'm developing an app for Android TV, and use DPAD navigation.
I have multiple widgets inside a column. when i navigate to a widget which is outside the view, the widget/view is not moving to reflect the selected widget.
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatelessWidget(),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
return DefaultTextStyle(
style: textTheme.headline4!,
child: ChangeNotifierProvider<SampleNotifier>(
create: (context) => SampleNotifier(), child: const CardHolder()),
);
}
}
class CardHolder extends StatefulWidget {
const CardHolder({Key? key}) : super(key: key);
#override
_CardHolderState createState() => _CardHolderState();
}
class _CardHolderState extends State<CardHolder> {
late FocusNode _focusNode;
late FocusAttachment _focusAttachment;
#override
void initState() {
super.initState();
_focusNode = FocusNode(debugLabel: "traversal_node");
_focusAttachment = _focusNode.attach(context, onKey: _handleKeyPress);
_focusNode.requestFocus();
}
#override
Widget build(BuildContext context) {
_focusAttachment.reparent();
return Focus(
focusNode: _focusNode,
autofocus: true,
onKey: _handleKeyPress,
child: Consumer<SampleNotifier>(
builder: (context, models, child) {
int listSize = Provider.of<SampleNotifier>(context).listSize;
return SingleChildScrollView(
child: SampleRow(cat: "Test", models: models.modelList),
);
},
),
);
}
KeyEventResult _handleKeyPress(FocusNode node, RawKeyEvent event) {
if (event is RawKeyDownEvent) {
print("t:FocusNode: ${node.debugLabel} event: ${event.logicalKey}");
if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
Provider.of<SampleNotifier>(context, listen: false).moveRight();
return KeyEventResult.handled;
} else if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
Provider.of<SampleNotifier>(context, listen: false).moveLeft();
return KeyEventResult.handled;
}
}
// debugDumpFocusTree();
return KeyEventResult.ignored;
}
}
class SampleCard extends StatefulWidget {
final int number;
final SampleModel model;
final bool focused;
const SampleCard(
{required this.number,
required this.focused,
required this.model,
Key? key})
: super(key: key);
#override
_SampleCardState createState() => _SampleCardState();
}
class _SampleCardState extends State<SampleCard> {
late Color _color;
#override
void initState() {
super.initState();
_color = Colors.red.shade900;
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: widget.focused
? Container(
width: 150,
height: 300,
color: Colors.white,
child: Center(
child: Text(
"${widget.model.text} ${widget.model.num}",
style: TextStyle(color: _color),
),
),
)
: Container(
width: 150,
height: 300,
color: Colors.black,
child: Center(
child: Text(
"${widget.model.text} ${widget.model.num}",
style: TextStyle(color: _color),
),
),
),
);
}
}
class SampleRow extends StatelessWidget {
final String cat;
final List<SampleModel> models;
SampleRow({Key? key, required this.cat, required this.models}) : super(key: key);
#override
Widget build(BuildContext context) {
final int selectedIndex =
Provider.of<SampleNotifier>(context).selectedIndex;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(left: 16, bottom: 8),
),
models.isNotEmpty
? SizedBox(
height: 200,
child: ListView.custom(
padding: const EdgeInsets.all(8),
scrollDirection: Axis.horizontal,
childrenDelegate: SliverChildBuilderDelegate(
(context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: SampleCard(
focused: index == selectedIndex,
model: models[index],
number: index,
),
),
childCount: models.length,
findChildIndexCallback: _findChildIndex,
),
),
)
: SizedBox(
height: 200,
child: Container(
color: Colors.teal,
),
)
],
);
}
int _findChildIndex(Key key) => models.indexWhere((model) =>
"$cat-${model.text}_${model.num}" == (key as ValueKey<String>).value);
}
class SampleNotifier extends ChangeNotifier {
final List<SampleModel> _models = [
SampleModel(0, "zero"),
SampleModel(1, "one"),
SampleModel(2, "two"),
SampleModel(3, "three"),
SampleModel(4, "four"),
SampleModel(5, "five"),
SampleModel(6, "six"),
SampleModel(7, "seven"),
SampleModel(8, "eight"),
SampleModel(9, "nine"),
SampleModel(10, "ten")
];
int _selectedIndex = 0;
List<SampleModel> get modelList => _models;
int get selectedIndex => _selectedIndex;
int get listSize => _models.length;
void moveRight() {
if (_selectedIndex < _models.length - 1) {
_selectedIndex = _selectedIndex + 1;
}
notifyListeners();
}
void moveLeft() {
if (_selectedIndex > 0) {
_selectedIndex = _selectedIndex - 1;
}
notifyListeners();
}
}
class SampleModel {
int num;
String text;
SampleModel(this.num, this.text);
}
I need a way to move/scroll the widget into view. Is there any way to do this, using the DPAD navigation on android tv
Here is the gist
You could use the scrollable_positioned_list package.
Instead of a ListView.custom which scrolls based on pixels, this widgets its based on index:
final ItemScrollController itemScrollController = ItemScrollController();
ScrollablePositionedList.builder(
itemCount: 500,
itemBuilder: (context, index) => Text('Item $index'),
itemScrollController: itemScrollController,
itemPositionsListener: itemPositionsListener,
);
So you could maintain an index of the current scroll position and on DPAD press just :
itemScrollController.jumpTo(index: currentItem);
setState((){currentItem++;})

When I add elements to listview, how to update listview in Flutter?

I am new to flutter and I would like to add element every 5 seconds to my list view. I have list view and I think I have the true adding method. However, I do not know how to update my list view every 5 seconds.
void randomCity(){
List <int> colors = [yellow,green,blue,red,black,white];
List <String> countryNames = ["Gdańsk","Warszawa","Poznań","Białystok","Wrocław","Katowice","Kraków"];
List <String> countryImages = [gdanskPic,warszawaPic,poznanPic,bialystokPic,wroclawPic,katowicePic,krakowPic];
Random random = new Random();
DateTime now = new DateTime.now();
Future.delayed(Duration(seconds: 5), (){
setState(() {
int randomCity = random.nextInt(countryNames.length);
int randomColor = random.nextInt(colors.length);
countrylist.add(Country(
countryNames[randomCity], countryImages[randomCity],
colors[randomColor], now.toString()));
});
});
}
In this code I am adding new element to my list view.
randomCity();
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
backgroundColor: Colors.grey[100],
elevation: 0.0,
title: Text(
"Random City App",
style: TextStyle(fontSize: 20.0, color: Colors.black),
),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.add,
color: Colors.black,
size: 32,
),
onPressed: () {})
],
),
body: ListView.builder(
itemCount: countrylist.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => CountryDetails(countryName: countrylist[index].name,
appBarColor: countrylist[index].color, date: countrylist[index].date, image: countrylist[index].image,))
);
},
title: Text(countrylist[index].name + " ${countrylist[index].date}"),
tileColor: Color(countrylist[index].color),
),
);
},
));
}
And this is my ListView.Builder.
You have to convert your widget into StatefulWidget and then rebuild it with setState (more info on ways to manage state https://flutter.dev/docs/development/data-and-backend/state-mgmt/options)
class MyApp extends StatelessWidget { // your main widget
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatefulWidget { // create new StatefulWidget widget
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
List<Country> countrylist = []; // mover other variables in here
...
void randomCity(){
...
setState(() {}); // this will rebuild your widget again and again
}
#override
Widget build(BuildContext context) {
Future.delayed(Duration(seconds: 5), (){
randomCity();
});
return ListView.builder(
itemCount: countrylist.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
onTap: () {},
title: Text(countrylist[index]),
),
);
},
);
}
}
You have to tell the ListView to rebuild which you can do with the setState method (if you are using a StefulWidget). Also, I would use Timer instead of Future.delayed for periodic updates. Here would be a simplified example of your usecase:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Timer timer;
final countryNames = ['Germany', 'Croatia', 'Turkey', 'USA'];
List<String> countryList = [];
#override
void initState() {
Timer.periodic(Duration(seconds: 5), (timer) {
int randomCity = Random().nextInt(countryNames.length);
countryList.add(countryNames[randomCity]);
setState(() {});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List Updater'),
),
body: ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Text(countryList[index]),
);
},
itemCount: countryList.length,
),
);
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
}

Flutter Trouble with multiselect checkboxes - data from Firestore

The following code displays the items listed in my collection (Firestore)
I am attempting to create the ability to check any item(s) and then have those items store into a "Favorites" on the next screen.
Currently, the checkboxes are an all or nothing. Either all items are unchecked or checked once tapped.
class _SelectScreenState extends State<SelectScreen> {
bool _isChecked = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Select Exercises')),
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('exercises').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot)
{
return ListView(
padding: const EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildListItem(context,
data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.name),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
child: ListTile(
title: Text(record.name),
trailing: Checkbox(
value: _isChecked,
onChanged: (bool value) {
setState(() {
_isChecked = value;
});
},
)
),
),
);
}
}
class Record {
final String name;
final DocumentReference reference;
Record(this.name, this.reference);
Record.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
name = map['name'];
Record.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Record<$name:>";
}
It is because you are making use of a single variable for all the checkboxes.
To fix that you could create a dedicated stateful widget, which would handle the state of each of the checkbox's separately from the rest.
So you could replace your ListTile with something like
Exercise(
title: record.name,
)
and then you could define the Exercise widget as follows
class Exercise extends StatefulWidget {
final String title;
Exercise({this.title});
#override
_ExerciseState createState() => _ExerciseState();
}
class _ExerciseState extends State<Exercise> {
bool selected = false;
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(widget.title),
trailing: Checkbox(
value: selected,
onChanged: (bool val) {
setState(() {
selected = val;
});
}),
);
}
}
Here is a complete working example
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: <Widget>[
Exercise(
title: "Exercises 1",
),
Exercise(
title: "Exercises 2",
),
],
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class Exercise extends StatefulWidget {
final String title;
Exercise({this.title});
#override
_ExerciseState createState() => _ExerciseState();
}
class _ExerciseState extends State<Exercise> {
bool selected = false;
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(widget.title),
trailing: Checkbox(
value: selected,
onChanged: (bool val) {
setState(() {
selected = val;
});
}),
);
}
}
Because you have a global variable _isChecked, this needs to be created with each listTile.
Try moving the variable
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
bool _isChecked = false; //try moving it here
return Padding(
key: ValueKey(record.name),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
child: ListTile(
title: Text(record.name),
trailing: Checkbox(
value: _isChecked,
onChanged: (bool value) {
setState(() {
_isChecked = value;
});
},
)
),
),
);
}