Every time setstate called, randomSel variable List get updated and I want to stop this
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyColorBook();
}
}
class MyColorBook extends State<MyApp> {
List randomSel = new List<int>.generate(16, (int index) => index + 1);
var accepted = List<bool>.generate(16, (i) => false);
initState() {
super.initState();
}
You can make it as a global variable by putting outside the class:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyColorBook();
}
}
List randomSel = new List<int>.generate(16, (int index) => index + 1);
var accepted = List<bool>.generate(16, (i) => false);
class MyColorBook extends State<MyApp> {
initState() {
super.initState();
}
or you can put that data in a class:
class Data{
List randomSel = new List<int>.generate(16, (int index) => index + 1);
var accepted = List<bool>.generate(16, (i) => false);}
and if you want to access that:
initialize Data:
Data data = Data();
and access the randomSel through:
data.randomSel
Related
Here is how the main TodoApp widget looks like:
class TodoApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(title: 'Todo List', home: new TodoList());
}
}
I have created a class for a list item which is called Task:
class Task {
String name;
int number;
Task(this.name, this.number);
}
The list widget looks like this:
class TodoList extends StatefulWidget {
#override
createState() => new TodoListState();
}
The TodoListState class has a method which adds new items to the list:
class TodoListState extends State<TodoList> {
List<Task> _todoItems = [];
List<bool> checkedItems = []; // this was created for testing purposes
void _addTodoItem(Task task) {
if (task.name.length > 0) {
setState(() => checkedItems.add(false)); // works fine
setState(() => _todoItems.add(new Task('abc', 12))); // does not work
}
}
...
}
Why when _addTodoItem method is called, one item is added to the checkedItems list but _todoItems list is left unchanged? What am I missing?
It's working as it should, setState is only called once, hence the first method is executed. You should add both methods in the setState method, since it's called once per run or state, you can write it like this:
....
setState(() => {
checkedItems.add(false);
_todoItems.add(new Task('abc', 12);
});
....
StatefulWidget to StatefulWidget
How to Change String TimeSetdata in class Test2 setstate
class Test1 extends StatefulWidget {
#override
_Test1State createState() => _Test1State();
}
class _Test1State extends State<Test1> {
String TimeSetdata = "9.00 AM";
#override
Widget build(BuildContext context) {
...
Text(TimeSetdata);
}
}
class Test2 extends StatefulWidget {
#override
_Test2State createState() => _Test2State();
}
class _Test2State extends State<Test2> {
#override
Widget build(BuildContext context) {
...
onPressed: () {
setState(() {
TimeSetdata = "11.00 AM";
});
};
}
}
How to setState in Class Test2 Change String TimeSetdata in Widget Text(TimeSetdata) to "11.00 AM";
Allow the parent to pass a callback function to the child and pass a callback, when called from the child, updates the value in the parent.
This assumes that Test2 is a child of Test1 (you didn't make this clear in your question)
typedef StringCallback = void Function(String);
class Test2 extends StatefulWidget {
Test2({#required this.onPressed});
final StringCallback onPressed;
#override
_Test2State createState() => _Test2State(onPressed: onPressed);
}
class _Test2State extends State<Test2> {
_Test2State({#required this.onPressed});
final StringCallback onPressed;
#override
Widget build(BuildContext context) {
...
onPressed: () => onPressed(),
}
}
class _Test1State extends State<Test1> {
String TimeSetdata = "9.00 AM";
#override
Widget build(BuildContext context) {
...
Test2(onPressed: (s) => setState(() => TimeSetdata = s),
...
Text(TimeSetdata);
}
}
Here is the summary of the code I'm having a problem with:
Parent widget
class HomePage extends StatefulWidget {
#override
State<HomePage> createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
final GlobalKey<AsyncLoaderState> _asyncLoaderState = GlobalKey<AsyncLoaderState>();
List<DateTime> rounds;
List<PickupModel> pickups;
DateTime currentDate;
Widget build(BuildContext context) {
var _asyncLoader = AsyncLoader(
key: _asyncLoaderState,
initState: () async => await _getData(),
renderLoad: () => Scaffold(body: Center(child: CircularProgressIndicator())),
renderError: ([error]) => Text('Sorry, there was an error loading'),
renderSuccess: ({data}) => _buildScreen(context),
);
return _asyncLoader;
}
Widget _buildScreen(context) {
return Scaffold(
body: PickupList(pickups),
);
}
Future<Null> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
);
if (picked != null && picked != currentDate) {
currentDate = picked;
pickups = await api.fetchPickupList(currentDate);
setState(() {
});
}
}
_getData() async {
rounds = await api.fetchRoundsList();
currentDate = _getNextRound(rounds);
pickups = await api.fetchPickupList(currentDate);
}
}
Children Widget
(Listview builds tiles)
class PickupTile extends StatefulWidget{
final PickupModel pickup;
PickupTile(this.pickup);
#override
State<StatefulWidget> createState() {
return PickupTileState();
}
}
class PickupTileState extends State<PickupTile> {
Duration duration;
Timer timer;
bool _isUnavailable;
bool _isRunning = false;
bool _isDone = false;
#override
void initState() {
duration = widget.pickup.duration;
_isUnavailable = widget.pickup.customerUnavailable;
super.initState();
}
#override
Widget build(BuildContext context) {
return Row(
children: [
// UI widgets
]
}
So I have a parent widget an initial list of pickups which are displayed in the children PickupTile. One can change the date of the pickups displayed using _selectDate. This fetches a new list of Pickups which are stored in the parent State, and the children are rebuilt with their correct attributes. However, the State of the children widget (duration, isRunning, isDone...) is not reset so they stay on screen when changing the date.
If feel like I'm missing something obvious but I can't figure out how to reset the State of the children Widget or create new PickupTiles so that when changing the date I get new separate States.
I am writing a base loadmore listview in flutter following the tutorial building-a-social-network-with-flutter:
Define a typedef function as a listview adapter, it return a widget for each item:
typedef Widget WidgetAdapter<T>(T t);
My base listview widget:
class LoadingListView<T> extends StatefulWidget{
...
final WidgetAdapter<T> widgetAdapter;
...
#override createState() => new _LoadingListViewState();
}
My base listview state:
class _LoadingListViewState<T> extends State<LoadingListView<T>> {
...
List<T> objects = [];
...
Widget itemBuilder(BuildContext context, int index) {
return widget.widgetAdapter != null ? widget.widgetAdapter(objects[index]);
: new Container();
}
}
In my UserList widget:
#override
Widget build(BuildContext context) {
Widget w;
w = new LoadingListView<User>(
request, widgetAdapter: adapt, pageSize: widget.pageSize, pageThreshold: widget.pageThreshold);
return w;
}
Widget adapt(User user){
return new UserItem(user); //return widget UserItem
}
And i am getting the error:
type '(User) => Widget' is not a subtype of type '(dynamic) => Widget'
How to fix it. Please help me. Thanks you very much.
There is a problem with createState() method in your StatefullWidget class.
Update your createState() method like this.
#override
State createState() => new _LoadingListViewState<T>()
I've been facing some problems related to the setState function while using Stateful Widgets that updates itself with the help of Timers. The code below show 2 main classes that replicate how I came to find this error. The Text Widget "Lorem" should be inserted within 10 seconds - and it is - but it's never shown. I tried to debug the array "Items" and it does contain the "lorem" Text Widget after 5 seconds, as it should. The "build" function runs but doesn't make any difference in the UI.
class textList extends StatefulWidget {
#override
State<StatefulWidget> createState() =>
new _textListState();
}
class _textListState extends State<textList>
with TickerProviderStateMixin {
List<Widget> items = new List();
Widget lorem = new textClass("Lorem");
Timer timer;
#override
void initState() {
super.initState();
items.add(new textClass("test"));
items.add(new textClass("test"));
timer = new Timer.periodic(new Duration(seconds: 5), (Timer timer) {
setState(() {
items.removeAt(0);
items.add(lorem);
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
Iterable<Widget> content = ListTile.divideTiles(
context: context, tiles: items).toList();
return new Column(
children: content,
);
}
}
class textClass extends StatefulWidget {
textClass(this.word);
final String word;
#override
State<StatefulWidget> createState() =>
new _textClass(word);
}
class _textClass extends State<textClass>
with TickerProviderStateMixin {
_textClass(this.word);
String word;
Timer timer;
#override
void initState() {
super.initState();
timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) {
setState(() {
word += "t";
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
return new Text(word);
}
}
This is not how I came to find this error but this is the simplest way to replicate it. The main idea is: The children texts should keep updating themselves (in this case, adding "t"s in the end) and, after 5 seconds, the last of them should be replaced for the Text Widget "Lorem", what does happen in the list but not in the UI.
Here's what's wrong:
A State should never have any constructor arguments. Use the widget property to get access to final properties of the associated StatefulWidget.
Flutter is reusing your _textClass instance because the class name and keys match. This is a problem since you only set widget.word in initState so you're not picking up the new word configuration information. You can fix this either by giving the StatefulWidget instances unique keys to disambiguate them and cause the old State to be disposed, or you can keep around the old State and implement didUpdateWidget. The latter approach is shown below.
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new Scaffold(
appBar: new AppBar(title: new Text('Example App')),
body: new textList(),
),
));
}
class textList extends StatefulWidget {
#override
State<StatefulWidget> createState() =>
new _textListState();
}
class _textListState extends State<textList>
with TickerProviderStateMixin {
List<Widget> items = new List();
Widget lorem = new textClass("Lorem");
Timer timer;
#override
void initState() {
super.initState();
items.add(new textClass("test"));
items.add(new textClass("test"));
timer = new Timer.periodic(new Duration(seconds: 5), (Timer timer) {
setState(() {
items.removeAt(0);
items.add(lorem);
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
Iterable<Widget> content = ListTile.divideTiles(
context: context, tiles: items).toList();
return new Column(
children: content,
);
}
}
class textClass extends StatefulWidget {
textClass(this.word);
final String word;
#override
State<StatefulWidget> createState() =>
new _textClass();
}
class _textClass extends State<textClass>
with TickerProviderStateMixin {
_textClass();
String word;
Timer timer;
#override
void didUpdateWidget(textClass oldWidget) {
if (oldWidget.word != widget.word) {
word = widget.word;
}
super.didUpdateWidget(oldWidget);
}
#override
void initState() {
super.initState();
word = widget.word;
timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) {
setState(() {
word += "t";
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
return new Text(word);
}
}