The function 'setState' isn't defined. Dart/Flutter - flutter

I want to be able to spawn a dropdown menu to select values, I've made sure everything is stateful but Setstate doesn't like it still.
Tried wrapping it in different widgets or using different dropdown properties. still doesn't want to work it seems.
class DicePage extends StatefulWidget {
#override
_DicePageState createState() => new _DicePageState();
}
class _DicePageState extends State<DicePage> {
#override
Widget build(BuildContext ctxt) {
return Scaffold(
appBar: AppBar(
),
body: BodyLayout(),
);
}
}
class BodyLayout extends StatefulWidget {
BodyLayout({Key key}) : super(key: key);
#override
_BodyLayoutState createState()=> _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout> {
#override
Widget build(BuildContext ctxt) {
return _myListView(ctxt);
}
}
Widget _myListView(BuildContext ctxt) {
final List<String> entries = <String>['A','B','C'];
final List<int> colorCodes = <int>[600, 500, 100];
return ListView.separated(
padding: const EdgeInsets.all(8.0),
itemCount: entries.length,
itemBuilder: (BuildContext ctxt, int index) {
return Container(
height: 150,
color: Colors.amber[colorCodes[index]],
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new SingleChildScrollView(
child: DropdownButton<String>(
value: dropdownValue,
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: <String>['1','2','3','4','5','6','7','8','9','10']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
new Container(
padding: const EdgeInsets.all(20),
child: new MaterialButton(
onPressed: () {},
child: Text('2'),
),
),
new Container(
padding: const EdgeInsets.all(20),
child: new IconButton(
iconSize: 80,
onPressed: () {},
icon: Icon(Icons.casino),
),
),
],
)
);
},
separatorBuilder: (BuildContext ctxt, int index) => const Divider(),
);
}
Expected it to spawn a listed dropdown menu on button click.

Trying your code, it will actually get the error:
The function 'setState' isn't defined.
If you call setState outside, there is no way for flutter to recognize on which state should be updated.
You should have the _myListView method inside the BodyLayout class, as that has to do with the state.
Here is an updated code:
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,
),
);
}
}
class DicePage extends StatefulWidget {
#override
_DicePageState createState() => new _DicePageState();
}
class _DicePageState extends State<DicePage> {
#override
Widget build(BuildContext ctxt) {
return Scaffold(
appBar: AppBar(),
body: BodyLayout(),
);
}
}
class BodyLayout extends StatefulWidget {
BodyLayout({Key key}) : super(key: key);
#override
_BodyLayoutState createState() => _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout> {
String dropdownValue;
#override
Widget build(BuildContext ctxt) {
return _myListView(ctxt);
}
Widget _myListView(BuildContext ctxt) {
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
return ListView.separated(
padding: const EdgeInsets.all(8.0),
itemCount: entries.length,
itemBuilder: (BuildContext ctxt, int index) {
return Container(
height: 150,
color: Colors.amber[colorCodes[index]],
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new SingleChildScrollView(
child: DropdownButton<String>(
value: dropdownValue,
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: <String>[
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10'
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
new Container(
padding: const EdgeInsets.all(20),
child: new MaterialButton(
onPressed: () {},
child: Text('2'),
),
),
new Container(
padding: const EdgeInsets.all(20),
child: new IconButton(
iconSize: 80,
onPressed: () {},
icon: Icon(Icons.casino),
),
),
],
));
},
separatorBuilder: (BuildContext ctxt, int index) => const Divider(),
);
}
}

Related

How to make Dynamic checkbox with Api data?

How to make using Listview.builder() and handle it click in flutter
Simple way to achieve your goal, try this
import 'package:flutter/material.dart';
class SimpleTest extends StatefulWidget {
const SimpleTest({Key? key}) : super(key: key);
#override
State<SimpleTest> createState() => _SimpleTestState();
}
class _SimpleTestState extends State<SimpleTest> {
List<int> selectedItem = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: 8,
shrinkWrap: true,
itemBuilder: (context, index) {
return Container(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: CheckboxListTile(
title: Text("Check Box$index"),
value: selectedItem.contains(index) ? true : false,
onChanged: (newValue) {
if (selectedItem.contains(index)) {
selectedItem.remove(index);
} else {
selectedItem.add(index);
}
setState(() {});
},
controlAffinity: ListTileControlAffinity.leading, // <-- leading Checkbox
),
)
],
),
);
}))
],
),
);
}
}

Additional filters in Flutter

I am writing an application on Flutter. I was able to make one filter with multiple selections.
But I want to have additional filters.
I used Multi-Select, but in the case of multiple filters, I don't know how to apply it
With what help can I implement this?
import 'package:flutter/material.dart';
class MainPage extends StatelessWidget {
#override
Widget build(context) => Scaffold(
appBar: AppBar(title: Text("f"),
backgroundColor: Colors.black),
drawer: MediaQuery.of(context).size.width < 500 ? Drawer(
child: HomePage(),
) : null,
body: SafeArea(
child:Center(
child: MediaQuery.of(context).size.width < 500 ? Content() :
Row(
children: [
Container(
width: 200.0,
child: HomePage()
),
Container(
width: MediaQuery.of(context).size.width-200.0,
child: Content()
)
]
)
)
)
);
}
List devices_list = ["First device", "Second device", "Third device", "Fourth device", "Fifth device", "Sixth device", "Seventh device", "Eighth device", "Ninth device"];
class Content extends StatelessWidget{
#override
Widget build(context) =>
Scaffold(
backgroundColor: Colors.white,
body: LayoutBuilder(
builder: (context, constraints){
return AnimatedContainer(
duration: Duration(milliseconds: 500),
color: Colors.white,
child: Center(
child: Container(
constraints: BoxConstraints(
maxWidth: 800,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),),
child: ListView.builder(
itemCount: devices_list.length,
itemBuilder: (BuildContext context, int index) {
return SizedBox (
height: 60,
key: Key(devices_list[index]),
child: Card(
shape: const RoundedRectangleBorder(
side: BorderSide(color: Colors.black,width: 3),
borderRadius: BorderRadius.all(Radius.circular(15))),
child: TextButton(
onPressed: (){},
child: ListTile(title: Text(devices_list[index]))),
)
);
}
))));
}));
}
class MultiSelect extends StatefulWidget {
final List<String> items;
const MultiSelect({Key? key, required this.items}) : super(key: key);
#override
State<StatefulWidget> createState() => _MultiSelectState();
}
class _MultiSelectState extends State<MultiSelect> {
// this variable holds the selected items
final List<String> _selectedItems = [];
// This function is triggered when a checkbox is checked or unchecked
void _itemChange(String itemValue, bool isSelected) {
setState(() {
if (isSelected) {
_selectedItems.add(itemValue);
} else {
_selectedItems.remove(itemValue);
}
});
}
// this function is called when the Cancel button is pressed
void _cancel() {
Navigator.pop(context);
}
// this function is called when the Submit button is tapped
void _submit() {
Navigator.pop(context, _selectedItems);
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Select Manufactures'),
content: SingleChildScrollView(
child: ListBody(
children: widget.items
.map((item) => CheckboxListTile(
value: _selectedItems.contains(item),
title: Text(item),
controlAffinity: ListTileControlAffinity.leading,
onChanged: (isChecked) => _itemChange(item, isChecked!),
))
.toList(),
),
),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: _cancel,
),
ElevatedButton(
child: const Text('Submit'),
onPressed: _submit,
),
],
);
}
}
// Implement a multi select on the Home screen
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<String> _selectedItemsManufactures = [];
void _showMultiSelectManufactures() async {
// a list of selectable items
// these items can be hard-coded or dynamically fetched from a database/API
final List<String> _items = [
'Apple',
'Samsung',
'Xiaomi',
'Nokia',
'Huawei',
'Alcatel'
];
final List<String>? results = await showDialog(
context: context,
builder: (BuildContext context) {
return MultiSelect(items: _items);
},
);
// Update UI
if (results != null) {setState(() {_selectedItemsManufactures = results;});}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// use this button to open the multi-select dialog
ElevatedButton(
child: const Text('Manufactures'),
onPressed: _showMultiSelectManufactures,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.black)
),
),
const Divider(
height: 5,
color: Colors.white,
),
// display selected items
Wrap(
children: _selectedItemsManufactures
.map((e) => Chip(
label: Text(e),
))
.toList(),
)
],
),
),
);
}
}
Addition. Addition. I would like changes in the Menu class to be reflected in the class MainPage.
class DevicesPage extends StatelessWidget {
#override
Widget build(context) => Scaffold(
appBar: AppBar(title: Text("IT"),
backgroundColor: Colors.black),
drawer: MediaQuery.of(context).size.width < 500 ? Drawer(
child: Menu(),
) : null,
body: SafeArea(
child:Center(
child: MediaQuery.of(context).size.width < 500 ? MainPage() :
Row(
children: [
Container(
width: 200.0,
child: Menu()
),
Container(
width: MediaQuery.of(context).size.width-200.0,
child: MainPage()
)
]
)
)
)
);
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Map<String, List<String>?> filters = {};
List<Phone> filteredPhones = phoneList;
#override
Widget build(BuildContext context) {
return Scaffold(
body: filteredPhones.isEmpty
? const Center(child: Text('No product', style: TextStyle(fontSize: 16),))
: ListView.builder(
itemCount: filteredPhones.length,
itemBuilder: (_, index) {
final currentPhone = filteredPhones[index];
return ListTile(
title: Text(currentPhone.name),
subtitle: Text('${currentPhone.brand}-${currentPhone.color}'),
trailing: Text('${currentPhone.operation_system}'),
);
}
),
);
}
}
class Menu extends StatefulWidget {
const Menu({Key? key}) : super(key: key);
#override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
Map<String, List<String>?> filters = {};
List<Phone> filteredPhones = phoneList;
void _filter() {
setState(() {
filteredPhones = phoneList;
filters.forEach((key, value) {
if((value ?? []).isNotEmpty) {
filteredPhones = filteredPhones.where((phone) {
switch(key) {
case 'brand':
return value!.contains(phone.brand);
case 'color':
return value!.contains(phone.color);
case 'operation_system':
return value!.contains(phone.operation_system);
return true;
default:
return false;
}
}).toList();
}
});
filters.clear();
Navigator.of(context).pop();
});
}
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key] ?? [];
if(checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
filters[key] = currentFilters;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('phones List'),
actions: [
IconButton(
icon: const Icon(Icons.filter_alt),
onPressed: () {
showDialog<Filter>(context: context, builder: (_) {
return SimpleDialog(
title: const Text('Filters',textAlign: TextAlign.center,),
contentPadding: const EdgeInsets.all(16),
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Select a brand'),
...brands.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'brand', el),
),
).toList(),
const Text('Select a operation_system'),
...operation_system.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'operation_system', el),
)
).toList(),
const Text('Select a colors'),
...colors.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'color', el),
),
).toList(),
const SizedBox(height: 24,),
ElevatedButton(onPressed: _filter, child: const Text('APPLY')),
],
),
],
);
});
},
),
],
),
body: filteredPhones.isEmpty
? const Center(child: Text('No product', style: TextStyle(fontSize: 16),))
: ListView.builder(
itemCount: filteredPhones.length,
itemBuilder: (_, index) {
final currentPhone = filteredPhones[index];
return ListTile(
title: Text(currentPhone.name),
subtitle: Text('${currentPhone.brand}-${currentPhone.color}'),
trailing: Text('${currentPhone.operation_system}'),
);
}
),
);
}
}
enter image description here
Well... you have many options. One way is to store all filters selected in a Map, like this:
final allFilters = {
"filterName1": "someValue",
"filterName2": "someOtherValue",
"fitlerName3": ["value1", "value2", "value3"]
}
And create a function that can handle each key of this map:
allFilter.forEach((key, value) {
switch(key) {
case "filterName1":
// ...some code
case "fitlerName2":
// ...more code
case "filterName3":
// ...much more code
}
})
You can check this demo project that I created https://github.com/felipeemidio/ListWithMultipleFilters

Flutter callbacks - Removing a widget in a list from child

I have this app where you type in some text and press a button which adds this text to a custom widget. Here is the code:
import 'dart:core';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 0;
TextEditingController noteSend = TextEditingController();
List<String> noteString = [];
#override
Widget build(BuildContext context) {
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(i,
noteRec: noteString[i], noteString: noteString, count: count));
return new Scaffold(
appBar: new AppBar(title: new Text('some title')),
body: Column(
children: <Widget>[
Container(
child: TextField(
controller: noteSend,
),
color: Colors.lightBlueAccent,
width: 150,
height: 50,
),
Expanded(
child: ListView(
children: children,
scrollDirection: Axis.vertical,
),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
noteString.insert(
noteString.length,
noteSend.text,
);
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final int index;
final String noteRec;
final List<String> noteString;
final int count;
InputWidget(this.index, {Key key, this.noteRec, this.noteString, this.count})
: super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
// <-- onLongPress
setState(() {
widget.noteString.removeAt(widget.index);
});
},
child: new Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Icon(
Icons.image,
size: 75,
)
],
),
Container(
margin: EdgeInsets.only(left: 80, right: 30),
child: Column(
children: <Widget>[
Text('Note'),
],
),
),
Column(
children: <Widget>[
Text("${widget.noteRec}"),
],
),
],
),
),
);
}
}
I've tried to wrap the custom which in a gesture detector and then add a onLongPress which removes the widget at the index but it's not working.
How can I remove the widget I long press on ?
Thank you
For this scenario, you'll need to use a callback. Because of the scope of the variables, you can't directly delete a variable from noteString in the InputWidget() due to scope, however, it HAS to be triggered by InputWidget because the index information is contained in that widget and has to be used to remove the items from the noteString List, as well as removing the InputWidget from the children List. Therefore, it's callback time.
Callbacks work like this:
1. define a variable that will receive a function in the child.
final Function(int) onDelete;
2. Call the function in the child and pass in the needed variable:
onLongPress: () {
widget.onDelete(widget.index);
},
3. Define the function in the parent THAT YOU WANT TO USE IN THE PARENT and then pass it to the child:
Function(int) onDeleteVar = (int val) {
setState(
() => {
noteString.removeAt(val),
count--,
children.removeAt(val),
},
);
};
children = List.generate(
count,
(int i) => new InputWidget(i,
noteRec: noteString[i],
noteString: noteString,
count: count,
onDelete: onDeleteVar));
Here's a dartpad to see it in action:
http://dartpad.dev/a25e9c402a90fefc778bcfac27aee242
And here's the code:
import 'dart:core';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
int count = 0;
TextEditingController noteSend = TextEditingController();
List<String> noteString = [];
#override
Widget build(BuildContext context) {
List<Widget> children;
Function(int) onDeleteVar = (int val) {
setState(
() => {
print("noteStringBefore: $noteString"),
print('childrenBefore: $children'),
print(val),
noteString.removeAt(val),
count--,
children.removeAt(val),
print("noteStringAfter: $noteString"),
print('childrenAfter $children'),
},
);
};
children = List.generate(
count,
(int i) => new InputWidget(i,
noteRec: noteString[i],
noteString: noteString,
count: count,
onDelete: onDeleteVar));
return new Scaffold(
appBar: new AppBar(title: new Text('some title')),
body: Column(
children: <Widget>[
Container(
child: TextField(
controller: noteSend,
),
color: Colors.lightBlueAccent,
width: 150,
height: 50,
),
Expanded(
child: ListView(
children: children,
scrollDirection: Axis.vertical,
),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
noteString.insert(
noteString.length,
noteSend.text,
);
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final int index;
final String noteRec;
final List<String> noteString;
final int count;
final Function(int) onDelete;
InputWidget(this.index,
{Key key, this.noteRec, this.noteString, this.count, this.onDelete})
: super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
// <-- onLongPress
widget.onDelete(widget.index);
},
child: new Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Icon(
Icons.image,
size: 75,
)
],
),
Container(
margin: EdgeInsets.only(left: 80, right: 30),
child: Column(
children: <Widget>[
Text('Note'),
],
),
),
Column(
children: <Widget>[
Text("${widget.noteRec}"),
],
),
],
),
),
);
}
}

Can someone check my Dart code and tell me where I'm making mistake in returning data from my screen as a ListView

I am stuck here for the past 20 days in returning data in my app from the other screen. I'm new to programming and need help. I've been searching through all the internet to find an answer related to my query but nothing is helping though. I ask my fellow SO guys to please help.
You can look at the entire code which I've made open here.
My code:
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () async {
final newList = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
setState(() {
return ListView.builder(
itemCount: newList.length,
itemBuilder: (context, index){
return Container(
child: Text('item: $newList'),
);
},
);
});
},
)
],
),
);
}
}
The screen where Navigator.pop() is used:
final Set saved = Set();
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
value: saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// __saved.add(context); // changed
// } else{ // changed
// __saved.remove(context); // changed
// } // changed
if (val == true) {
saved.add(index);
} else {
saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text(nameOfSite[index]),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
),
);
}
}
Here is the SecondPage and FavoriteList that I made
import 'package:flutter/material.dart';
import 'package:aioapp2/lists.dart';
Set<int> favorites = {};
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
_getFavoriteList(),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FloatingActionButton(
child: Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditFavorites(),
),
).then((updatedFavorites) {
if (updatedFavorites != null)
setState(() {
favorites = updatedFavorites;
});
});
},
),
),
)
],
);
}
Widget _getFavoriteList() {
if (favorites?.isNotEmpty == true)
return _FavoriteList();
else
return _EmptyFavoriteList();
}
}
class _EmptyFavoriteList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Add Your Favorite Sites Here!❤',
style: TextStyle(color: Colors.white),
),
Icon(
Icons.favorite,
size: 150,
color: Colors.blue[100],
),
],
),
),
),
),
],
);
}
}
class _FavoriteList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: favorites.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('lib/images/${images[index]}'),
),
title: Text(nameOfSite[favorites.elementAt(index)]),
);
},
);
}
}
//Its FavoriteList Page. I changed the name
class EditFavorites extends StatefulWidget {
#override
_EditFavoritesState createState() => _EditFavoritesState();
}
class _EditFavoritesState extends State<EditFavorites> {
final _editableFavorites = <int>{};
#override
void initState() {
_editableFavorites.addAll(favorites);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red,
actions: <Widget>[
IconButton(
icon: Icon(Icons.done),
onPressed: () {
Navigator.pop<Set>(context, _editableFavorites);
},
)
],
),
//backgroundColor: Colors.indigo,
body: SafeArea(
child: ListView.builder(
itemCount: nameOfSite.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('lib/images/${images[index]}'),
),
title: Text(nameOfSite[index]),
trailing: IconButton(
icon: _editableFavorites.contains(index)
? Icon(
Icons.favorite,
color: Colors.red,
)
: Icon(
Icons.favorite_border,
color: Colors.grey,
),
onPressed: () {
setState(() {
if (_editableFavorites.contains(index))
_editableFavorites.remove(index);
else
_editableFavorites.add(index);
});
},
),
);
},
),
),
);
}
}
Just replace secondtab.dart with this code.
You can copy paste run full code below
You have to move out return ListView to the same layer with FloatingActionButton
working demo
full code
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: SecondPage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
Set newList = {};
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
ListView.builder(
itemCount: newList.length,
itemBuilder: (context, index) {
return Container(
child: Text('item: ${newList.elementAt(index)}'),
);
},
),
FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () async {
newList = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
setState(() {});
},
)
],
),
);
}
}
final Set saved = Set();
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
value: saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// __saved.add(context); // changed
// } else{ // changed
// __saved.remove(context); // changed
// } // changed
if (val == true) {
saved.add(index);
} else {
saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
//Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text('nameOfSite[index]'),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
),
);
}
}

How to populate a form from a listview on tap in flutter

I have a form widget, a list widget, and a "wrapper" widget or in other words, a parent/container widget. So to give an idea of the widget tree, it is as such.
Parent/Container Widget
Form Widget
Button Widget
List Widget
Notice that the form, buttons and list widget are all siblings, inside of the parent/container widget. What I want to happen, is tap on a list item in the list widget, and populate the form widget with the data that gets passed from the list widget.
Here is my parent widget.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:andplus_flutter_7_gui/services/user_service.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'crud_form.dart';
import 'crud_list.dart';
class Crud extends StatefulWidget {
Crud({Key key, this.title}) : super(key: key);
final String title;
_CrudContainerState createState() => _CrudContainerState();
}
class _CrudContainerState extends State<Crud> {
List<User> users;
User user = User();
UserService userService;
#override
void initState() {
super.initState();
if (userService == null) {
userService = UserService(user);
}
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
userService.dispose();
}
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text(widget.title),
),
body: Builder(
builder: (context) => Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 2,
child: StreamBuilder(
builder: (context, AsyncSnapshot<User> snapshot) {
return CrudForm(
user: snapshot.data,
onUserAdded: (user) {
userService.addUser(user);
},
);
},
stream: userService.userObservable,
),
),
Expanded(
child: Text("Future button widget"),
),
Expanded(
flex: 3,
child: StreamBuilder(
builder: (ctx, AsyncSnapshot<List<User>> snap) {
return CrudList(
onUserSelected: userService.userSelected,
users: snap.data,
);
},
stream: userService.usersObservable,
),
),
],
),
),
),
);
}
void onEditUser(User user) {
setState(() {
user = user;
});
}
}
The above widget wraps the three widgets I mentioned.
Here are the children widget:
Form:
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudForm extends StatefulWidget {
CrudForm({Key key, this.onUserAdded, this.user}) : super(key: key);
final User user;
final void Function(User user) onUserAdded;
_CrudFormState createState() => _CrudFormState(user: user);
}
class _CrudFormState extends State<CrudForm> {
_CrudFormState({this.user});
User user = User();
var _key = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Container(
child: Builder(
builder: (context) => Container(
color: Colors.blueAccent[100],
child: Form(
key: _key,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text(
"First Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
initialValue: widget.user?.firstName == null ||
widget.user.firstName.isEmpty
? user.firstName
: widget.user.firstName,
validator: (value) {
if (value.isEmpty) {
return "First name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.firstName = value;
});
},
),
),
)
],
),
Row(
children: <Widget>[
Text(
"Last Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
validator: (value) {
if (value.isEmpty) {
return "Last name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.lastName = value;
});
},
),
),
),
],
),
RaisedButton(
child: Text(
"Save",
),
splashColor: Colors.blueGrey,
onPressed: () {
if (!_key.currentState.validate()) {
return;
}
_key.currentState.save();
widget.onUserAdded(
new User(
firstName: user.firstName,
lastName: user.lastName,
),
);
},
)
],
),
),
),
),
),
);
}
}
Here is my list widget.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudList extends StatefulWidget {
CrudList({Key key, this.users, this.onUserSelected}) : super(key: key);
final List<User> users;
final SelectUser onUserSelected;
_CrudListState createState() => _CrudListState();
}
class _CrudListState extends State<CrudList> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
child: ListView.builder(
itemCount: widget.users?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
var user = widget.users[index];
return ListTile(
key: Key(index.toString()),
title: Center(
child: Text(
"${user.firstName} ${user.lastName}",
style: TextStyle(color: Colors.white),
),
),
onTap: () {
print("${widget.users[index]} $index");
widget.onUserSelected(widget.users[index]);
},
);
},
),
);
}
}
typedef void SelectUser(User user);
And just for further context, here is my user service, responsible for adding the objects to the stream, and using the stream builder within rxdart to notify of state changes.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:rxdart/rxdart.dart';
class UserService {
User _editedUser = User();
List<User> _users = <User>[];
BehaviorSubject<User> _userSubject;
BehaviorSubject<List<User>> _usersSubject;
UserService(this._editedUser) {
_userSubject = BehaviorSubject<User>.seeded(_editedUser);
_usersSubject = BehaviorSubject<List<User>>.seeded(_users);
}
Observable<List<User>> get usersObservable => _usersSubject.stream;
Observable<User> get userObservable => _userSubject.stream;
addUser(User user) {
_users.add(user);
_usersSubject.add(_users);
}
dispose() {
_userSubject.close();
_usersSubject.close();
}
void userSelected(User user) {
_editedUser = user;
_userSubject.add(_editedUser);
}
}
What am I missing? It looks like my widget rebuilds, and tries to set the initial value in the form when I tap the user in the list widget. But the actual field doesn't get updated and I'm not sure why.
I'd appreciate any documentation or articles on how to better approach data and state management between sibling widgets within the flutter framework.
Here's a similar use case that I tried to implement locally. What I'm doing here is I generate TextFormFields dynamically and assign TextEditingController.
Column textField(int n) {
List<Widget> listForm = [];
while (n > 0) {
var textEditingController = TextEditingController();
listForm.add(
TextFormField(
controller: textEditingController,
onTap: () {
_selectedField = textEditingController;
},
),
);
n--;
}
return Column(children: listForm);
}
Clicking a ListView item updates the text of the currently selected TextFormField.
InkWell(
onTap: () {
debugPrint('Selected $index!');
if (_selectedField != null) {
_selectedField!.value = TextEditingValue(text: 'Item $index');
}
},
child: ListTile(
title: Text('Item $index'),
),
);
Complete sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController? _selectedField;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(flex: 1, child: textField(3)),
Expanded(flex: 1, child: listItems()),
],
),
),
),
);
}
Column textField(int n) {
List<Widget> listForm = [];
while (n > 0) {
var textEditingController = TextEditingController();
listForm.add(
TextFormField(
controller: textEditingController,
onTap: () {
debugPrint('Current Controller: $textEditingController');
_selectedField = textEditingController;
},
),
);
n--;
}
return Column(children: listForm);
}
ListView listItems() {
return ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
if (_selectedField != null) {
_selectedField!.value = TextEditingValue(text: 'Item $index');
}
},
child: ListTile(
title: Text('Item $index'),
),
);
},
);
}
}
Demo