MyStateObject was disposed when I navigated back from Screen2 to Screen1 - flutter

I will post my projects minimum classes here that you can reproduce the faulty behavior.
The listing of the classes here goes mostly from the top of the flutter widget hierarchy down the rest...
main.dart
import 'package:TestIt/widgets/applicationpage.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final ApplicationPage applicationPage =
ApplicationPage(title: 'Flutter Demo');
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
primarySwatch: Colors.blue,
),
home: applicationPage);
}
}
applicationpage.dart
import 'package:flutter/material.dart';
import 'body.dart';
class ApplicationPage extends StatefulWidget {
ApplicationPage({Key key, this.title}) : super(key: key);
final String title;
#override
_ApplicationPageState createState() => _ApplicationPageState();
}
class _ApplicationPageState extends State<ApplicationPage> {
final Body body = new Body();
#override
Widget build(BuildContext context) {
return Scaffold(
body: body);
}
}
body.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:TestIt/viewmodels/workout.dart';
import 'package:flutter/material.dart';
import 'Excercises/ExcerciseListWidget.dart';
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
var workouts = new List<Workout>();
var pullDay = new Workout("Pull day", new List<Excercise>());
workouts.add(pullDay);
return Container(
margin: EdgeInsets.all(5),
child: DefaultTabController(
// Added
length: workouts.length, // Added
initialIndex: 0, //Added
child: Scaffold(
appBar: PreferredSize(
// todo: add AppBar widget here again
preferredSize: Size.fromHeight(50.0),
child: Row(children: <Widget>[
TabBar(
indicatorColor: Colors.blueAccent,
isScrollable: true,
tabs: getTabs(workouts),
),
Container(
margin: EdgeInsets.only(left: 5.0),
height: 30,
width: 30,
child: FloatingActionButton(
heroTag: null,
child: Icon(Icons.add),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
elevation: 5.0,
onPressed: () => print("add workout"))),
Container(
margin: EdgeInsets.only(left: 5.0),
height: 30,
width: 30,
child: FloatingActionButton(
heroTag: null,
child: Icon(Icons.remove),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
elevation: 5.0,
onPressed: () => print("add workout"))),
])),
body: TabBarView(
children: getTabViews(workouts),
),
)));
}
List<ExcerciseListWidget> getTabViews(List<Workout> workouts) {
var tabViews = new List<ExcerciseListWidget>();
for (var i = 0; i < workouts.length; i++) {
tabViews.add(ExcerciseListWidget(workouts[i].excercises));
}
return tabViews;
}
List<Tab> getTabs(List<Workout> workouts) {
Color textColor = Colors.blueAccent;
return workouts
.map((w) => new Tab(
child: Text(w.name, style: TextStyle(color: textColor)),
))
.toList();
}
}
ExcerciseListWidget.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:flutter/material.dart';
import 'ExcerciseWidget.dart';
class ExcerciseListWidget extends StatefulWidget {
ExcerciseListWidget(this.excercises);
final List<Excercise> excercises;
#override
_ExcerciseListWidgetState createState() => _ExcerciseListWidgetState();
}
class _ExcerciseListWidgetState extends State<ExcerciseListWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widget.excercises.insert(
0,
new Excercise(widget.excercises.length + 1, "test",
widget.excercises.length * 10));
});
},
child: Icon(Icons.add),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
elevation: 5.0,
),
body: Container(
padding: const EdgeInsets.all(2),
child: ReorderableListView(
onReorder: (index1, index2) => {
print("onReorder"),
},
children: widget.excercises
.map((excercise) => ExcerciseWidget(
key: ValueKey(excercise.id), excercise: excercise))
.toList())));
}
}
ExcerciseWidget.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'ExcerciseDetailsWidget.dart';
class ExcerciseWidget extends StatefulWidget {
ExcerciseWidget({this.key, this.excercise}) : super(key: key);
final Excercise excercise;
final Key key;
#override
_ExcerciseWidgetState createState() => _ExcerciseWidgetState();
}
class _ExcerciseWidgetState extends State<ExcerciseWidget> {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 3.0, bottom: 3.0),
// TODo: with this ink box decoration the scrolling of the excercises goes under the tabbar... but with the ink I have a ripple effect NOT under
// the element...
child: Ink(
decoration: BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(5.0)),
border: Border.all(color: Colors.orange),
color: Colors.green),
child: InkWell(
onTap: () => {navigateToEditScreen(context)},
child: Column(
children: <Widget>[
Container(
color: Colors.red, child: Text(widget.excercise.name)),
],
)),
));
}
navigateToEditScreen(BuildContext context) async {
final Excercise result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ExcerciseDetailsWidget(excercise: widget.excercise)));
setState(() {
widget.excercise.name = result.name;
});
}
}
ExcerciseDetailsWidget.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:flutter/material.dart';
class ExcerciseDetailsWidget extends StatefulWidget {
final Excercise excercise;
ExcerciseDetailsWidget({Key key, #required this.excercise}) : super(key: key);
#override
_ExcerciseDetailsWidgetState createState() => _ExcerciseDetailsWidgetState();
}
class _ExcerciseDetailsWidgetState extends State<ExcerciseDetailsWidget> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.excercise.name),
),
body: Padding(
padding: EdgeInsets.only(left: 20, right: 20, bottom: 2, top: 2),
child: Form(
key: _formKey,
child: Column(children: <Widget>[
new RaisedButton(
elevation: 2,
color: Colors.blue,
child: Text('Save'),
onPressed: () {
setState(() {
widget.excercise.name = "new name";
});
Navigator.pop(context, widget.excercise);
}),
TextFormField(
decoration: InputDecoration(
//hintText: 'excercise name',
labelText: 'Excercise name',
),
initialValue: widget.excercise.name,
),
]))));
}
}
workout.dart
import 'excercise.dart';
class Workout{
Workout(this.name, this.excercises);
String name;
List<Excercise> excercises;
}
excercise.dart
class Excercise {
int id;
Excercise(this.id,this.name, this.restBetweenSetsInSeconds);
String name;
int restBetweenSetsInSeconds;
}
How to reproduce the faulty behavior to get the exception:
Click on the bottom-right floating action button to create an excercise test stub which is added to the only existing workout.
Click the newly added excercise
The ExcerciseDetailsWidget is loaded
Click Save in the ExcerciseDetailsWidget
Navigation goes back to the Initial screen and the Exception hits you in the face bam!
Exception
FlutterError (setState() called after dispose(): _ExcerciseWidgetState#bccdb(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().)
Question
Why is the formerly added and clicked ExcerciseWidget`s State disposed when I returned from the ExcerciseDetailsWidget ?
Check for is mounted and then call setState is no solution because in any case the excercise should NOT be disposed because I have to update it with the new excercise name.
If you know a flutter online site where I can put the project I will do so please let me know!
I am a flutter beginner maybe I do something completely wrong bear that in mind :-)
UPDATE
What I have done to workaround the problem is:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ExcerciseDetailsWidget(excercise: widget.excercise)));
do not await the result of the Navigator.
Instead I do this in the Screen2:
onPressed: () {
if (_formKey.currentState.validate()) {
// WHY can I set here the new text WITHOUT setState but when I navigated back the new excercise name is reflected in the list of excercises. Actually that should not be the case right? That confuses me totally.
widget.excercise.name =
excerciseNameTextController.value.text;
Navigator.pop(context);
}
},
but this is really just a workaround that works in this special EDIT use case.
When I have an ADD use case I need to return something to add it to the list of excercises...
Could it be that the problem is that I await the result inside the excercise?
I guess I will try to await the result excercise on the context/level of the ExercerciseListWidget not inside the ExcerciseWidget.
UPDATE 2
Reading more about the navigator it seems or could be that when I am navigating back to the former route which is my initial/root that all the knowledge about the clicked excercise is gone? Do I need therefore kind of nested routing? like "/workouts/id/excercises/id" ?

Despite the downvotes, this is a legitimate question. After poking around a little bit, the reason seems to be the ReorderableListView. For some reason, even if you are providing keys to each child of the list, when the ReorderableListView is rebuilt, all of its children are disposed and reinitialized. Because of this, when you navigate back from ExcerciseDetailsWidget, you are calling setState within a state that has been disposed - this is why you are getting that specific exception.
Frankly, your current code makes it very difficult to figure out whether it's something you've done wrong or a bug related to ReorderableListView. The only thing that can be said for sure is that replacing the ReorderableListView with a regular ListView will fix it.
I highly recommend cleaning up your code first - my IDE lit up like a Christmas tree when I copied your code in. Get rid of the new keyword. Use const constructors. Fix the Excercise typo that repeats itself 60 times in 250 rows of code.
And most importantly, given that you are mutating and displaying a data object across multiple stateful widgets, start using Provider for state management.

Related

Update child from parent in Flutter

Currently, I have two sample files Parent.dart and Child.dart.
In Parent.dart file this is what the code is like:
Parent.dart file:
children:
[
isDisabled
? Icon(Icons.public, color: Colors.grey)
: Icon(Icons.public, color:Colors.white),
InkWell(
onTap:()=> Navigator.push(context,MaterialPageRoute(builder: (context)=> Child(
isDisabled: isDisabled, function: ()=> function())),
]
function()
{
setState(()=> isDisabled = !isDisabled);
}
and in Child.dart the code is something like this:
children:
[
widget.isDisabled
? Icon(Icons.public, color: Colors.grey)
: Icon(Icons.public, color:Colors.white),
InkWell(
onTap:()=> widget.function(),
]
I have some data being fetched from a server that is used to populate a list of cards inside listview.builder.
What I'm trying to do is inherent variables from the parent and use their value to update the child. Currently, if I run this parent does change, but the child doesn't until you navigate back from parent to child.
For a better context: Imagine a list of cards. Each has an add-to-list button. Now if you click on the card it goes to another screen "child.dart" where it gives you more details about the item on the card you clicked. Now if you click the add-to-list button on the child screen it should also update the parent.
I tried different ways of achieving this "UI synchrony" for a better user experience. But I didn't find a proper way to implement it.
Things I tried: Provider (but it updates all the items on the list instead of each instance.),
a "hacky" method of editing the data in the list on the client side and updating the widget based on that. (This technique does work, but ewwwww)
I'm not really sure to understand your question.
If your question is how trigger a function in parent from child screen, here is your answer.
I made a working example. I think you were really close.
Another option for state management is riverpod 2.0
Or you can pass value in Navigator.pop and trigger the function in parent.
Parent model
class Parent {
String title;
bool isDisabled = false;
Parent({required this.title});
}
Main.dart
import 'package:flutter/material.dart';
import 'parent.dart';
import 'ParentCard.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({super.key});
List<Parent> parentList = [Parent(title: 'Item 1'), Parent(title: 'Item 2')];
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ListView.builder(
itemCount: parentList.length,
itemBuilder: (BuildContext context, int position) {
return ParentCard(title: parentList[position].title);
},
),
),
);
}
}
ParentCard.dart
import 'package:flutter/material.dart';
import 'child.dart';
class ParentCard extends StatefulWidget {
String title;
ParentCard({super.key, required this.title});
#override
State<ParentCard> createState() => _ParentCardState();
}
class _ParentCardState extends State<ParentCard> {
bool isDisabled = false;
#override
Widget build(BuildContext context) {
return Row(
children: [
Text(widget.title),
isDisabled
? Icon(Icons.public, color: Colors.green)
: Icon(Icons.public, color: Colors.black),
IconButton(
icon: Icon(Icons.plus_one),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ChildCard(isDisabled: isDisabled, handler: handler)),
),
)
],
);
}
handler() {
setState(() => isDisabled = !isDisabled);
}
}
** child.dart**
import 'package:flutter/material.dart';
class ChildCard extends StatefulWidget {
VoidCallback handler;
bool isDisabled;
ChildCard({super.key, required this.isDisabled, required this.handler});
#override
State<ChildCard> createState() => _ChildCardState();
}
class _ChildCardState extends State<ChildCard> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
const Text('child !'),
widget.isDisabled
? const Icon(Icons.public, color: Colors.green)
: const Icon(Icons.public, color: Colors.black),
ElevatedButton(
onPressed: () {
setState(() {
widget.isDisabled = !widget.isDisabled;
});
widget.handler();
},
child: const Text('click to trigger'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('pop it'),
)
]),
);
}
}

How to add a container to a column on tap in Flutter

I have a situation where I have a listview of containers, and I would like when I double tap each container another container pops up below with information. Currently what I am trying to do is wrap each container within a column and do something like:
onDoubleTap() {showBox = true}, and in the column have code:
children: [post(), showbox == true? infobox() : container()] but I am not sure of the correct implementation. Any help would be great!
you should maintain a list of containers:
class ContainerAdder extends StatefulWidget {
const ContainerAdder({Key? key}) : super(key: key);
#override
_ContainerAdderState createState() => _ContainerAdderState();
}
class _ContainerAdderState extends State<ContainerAdder> {
List<Widget> containers = <Widget>[];
Random random = Random();
List<Color> colors = [
Colors.blue,
Colors.green,
Colors.red,
Colors.orange,
Colors.purple,
Colors.pink,
Colors.teal,
Colors.yellow,
];
addContainer() {
setState(() {
int r = random.nextInt(colors.length);
containers.add(
InkWell(
onDoubleTap: () => addContainer(),
child: Container(
margin: const EdgeInsets.only(bottom: 1.0),
height: 50.0,
color: colors[r],
),
),
);
});
}
#override
void initState() {
super.initState();
addContainer();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [...containers],
),
);
}
}
As you can notice, the method addContainer() adds a container which is wrapped in an InkWell to have the tap listener. The doubleTap calls the method addContainer().
I simply spread the containers list inside ListView widget.
In the addContainer() method, I wrap the codes inside setState() so as to refresh the tree. You can use any other state management architecture if you so wish.
For the first time, I call addContainer() inside initState(), in order to populate the list with the first element.

setState() doesn't update constructor values in TabBarView tabs

I have a TabBarView with two tabs in main widget. First tab includes gridview with cards. Cards use parent widget (MyHomePage) as listener to listen in-card button clicks.
When i click on button in some card, listener impl. must open second Tab and pass selected Excursion to it. But when I do it, at first iteration, ExcursionEditor(currentExcursion) says, that argument is null, but parent build says, that it is not. If I resize my browser, it calls global rebuild and currentExcursion reach last build value.
So, i cant understand, why MyHomePage build doesn't affect on TabBarView content with arguments passed by constructor
class MyHomePage
import 'package:flutter/material.dart';
import 'package:questbuilder/api/content_manager.dart';
import 'package:questbuilder/model/excursion.dart';
import 'package:questbuilder/pages/tab_editor.dart';
import 'package:questbuilder/pages/tab_my_excursions.dart';
import 'package:questbuilder/widgets/excursion_preview_card.dart';
import 'package:logger/logger.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with TickerProviderStateMixin
implements ExcursionCardInteractionListener {
Logger logger = Logger();
Excursion currentExcursion;
TabController tabController;
#override
void initState() {
super.initState();
print("INIT STATE FOR HOME PAGE");
tabController = TabController(vsync: this, length: 2);
}
#override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
print("HOME PAGE BUILD currentExcursion = ${currentExcursion?.toJson()}");
return Scaffold(
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: Size(screenSize.width, 1000),
child: Container(
color: Colors.black,
child: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 30, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(children: [
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: Text('QUESTBUILDER',
style: TextStyle(color: Colors.white))),
SizedBox(width: screenSize.width / 20),
Container(
width: screenSize.width / 6,
child: TabBar(
labelPadding: EdgeInsets.fromLTRB(10, 0, 10, 10),
indicatorColor: Colors.white,
controller: tabController,
tabs: [
Tab(text: "Мои экскурсии"),
Tab(text: "Редактор"),
]))
]),
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
child: Row(
children: [
FlatButton.icon(
label: Text("Создать экскурсию"),
icon: Icon(Icons.add),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40.0)),
textColor: Colors.white,
color: Colors.green,
onPressed: () {
createExcursion();
}),
SizedBox(
width: 40,
),
InkWell(
onTap: () {},
child: Text(
'Вход',
style: TextStyle(color: Colors.white),
),
)
],
)),
],
),
),
),
),
body: Padding(
padding: EdgeInsets.all(15),
child: TabBarView(
controller: tabController,
children: [
// Set listener to cards in this widget to prerform 'edit' clicks
MyExcursionsTab(this),
ExcursionEditor(currentExcursion)
],
)));
}
// Here i call setState from cards
#override
void editExcursion(Excursion excursion) {
setState(() {
currentExcursion = excursion;
});
tabController.animateTo(1);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
void createExcursion() {
ContentManager.client.createExcursion(0).then((value) {
currentExcursion = value;
editExcursion(currentExcursion);
});
}
}
class ExcursionEditor
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:questbuilder/api/content_manager.dart';
import 'package:questbuilder/model/excursion.dart';
import 'package:questbuilder/model/excursion_content.dart';
import 'package:questbuilder/model/excursion_data.dart';
import 'package:questbuilder/model/picture.dart';
class ExcursionEditor extends StatefulWidget {
Excursion excursion;
ExcursionEditor(this.excursion);
#override
State<StatefulWidget> createState() => ExcursionEditorState();
}
class ExcursionEditorState extends State<ExcursionEditor> {
ExcursionData currentData;
ExcursionContent currentContent;
Excursion excursion;
List<Picture> pictures = [];
#override
void initState() {
super.initState();
print("INIT EDITOR widget.excrusion = ${widget.excursion?.toJson()}");
// At this point, after call setState() in HomePage widget.excrusion is always null
// until I resize browser, thereby calling global state reset
//
if (widget.excursion != null)
ContentManager.client.getPictureList(widget.excursion.id).then((value) {
pictures.addAll(value);
print(pictures);
});
}
#override
Widget build(BuildContext context) {
excursion = widget.excursion;
print("BUILD EDITOR excursion = ${widget.excursion?.toJson()}");
return excursion != null
? Container()
: Container(
child: Align(
alignment: Alignment.center,
child: Text("Выберите экскурсию для редактирования")));
}
}
Log of first launch and card click build sequence:
HOME PAGE BUILD currentExcursion = null
HOME PAGE BUILD currentExcursion = {id: 1}
INIT EDITOR widget.excrusion = null
BUILD EDITOR excursion = null
After browser window resize
HOME PAGE BUILD currentExcursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
HOME PAGE BUILD currentExcursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
After screen resize problem still appear, just replacing null value in editor with old Excursion. New clicks on cards doesn't have effect, setState in callback still not update.
I've tried to bind it on static stream listeners, on TabController listener - it just look like TabBarView late for 1 build cycle of arguments update. Maybe there are some similar questions, but i've done all from thouse answers and got nothing
I am not really sure, but it seems like race condition between setState and _tabController.animateTo(1); because they both try to rebuild the child ExcursionEditor(currentExcursion)
If you print the excursion in ExcursionEditor constructor, you will see the updated value. But at the end the value not reach the build function.
The simple workaround is changing editExcursion to the async function and add a small delay between this 2 actions. Otherwise you can try to use other way to pass data between widgets (like provider)
#override
Future editExcursion(Excursion excursion) async {
setState(() {
currentExcursion = excursion;
});
await Future.delayed(Duration(milliseconds:50));
tabController.animateTo(1);
}

How to fix error Concurrent modification during iteration: Instance(length:0) of '_GrowableList'

I'm trying to create a little card "game", and for now am trying to simply reset my card List when user hits the available button.
I'm having the following error message when hitting the "Reiniciar" button. I've tried to reinitialize just by stating _cardsAux=_cards but it does nothing...So I tried _cardsAux.addAll(_cards), which returns the error. I know that if I hard code my cards list onto the cardAux variable, it works, but i don't want that...What can I do to fix this and reach the desired result of resetting the card list so the user can keep playing?
> ======== Exception caught by gesture =============================================================== The following ConcurrentModificationError was thrown while handling a
> gesture: Concurrent modification during iteration: Instance(length:0)
> of '_GrowableList'.
>
> When the exception was thrown, this was the stack:
> #0 List.addAll (dart:core-patch/growable_array.dart:215:11)
> #1 _MyHomePageState._getACard.<anonymous closure> (package:our_cards/main.dart:102:29)
> #2 State.setState (package:flutter/src/widgets/framework.dart:1269:30)
> #3 _MyHomePageState._getACard (package:our_cards/main.dart:96:5)
> #4 _MyHomePageState.build.<anonymous closure> (package:our_cards/main.dart:163:36) ... Handler: "onTap" Recognizer:
> TapGestureRecognizer#3ba91 debugOwner: GestureDetector state:
> possible won arena finalPosition: Offset(218.7, 456.4)
> finalLocalPosition: Offset(60.4, 25.6) button: 1 sent tap down
my main.dart code
import 'package:flutter/material.dart';
import 'dart:math';
int random(min, max){
var rn = new Random();
return min + rn.nextInt(max - min);
}
final _cards = [
{'card':'Card A'},
{'card':'Card B'},
{'card':'Card C'}
];
var _card ='Press arrow Icon below to Start';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Our Cards',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Our Cards'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int pop;
var _cardsAux = _cards ;
void _getACard(bool isReset) {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
if(isReset){_cardsAux.addAll(_cards);}
if(_cardsAux.length== 0){
_card = 'No More Cards';
}
else {
pop = random(0, _cardsAux.length);
_card = _cardsAux[pop]['card'];
_cardsAux.removeAt(pop);
}
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(30),
child:
Text('$_card',
style: Theme.of(context).textTheme.headline3,
)
),
Container(
padding: EdgeInsets.all(30),
child: FlatButton(
color: Colors.blue,
textColor: Colors.white,
padding: EdgeInsets.all(8.0),
splashColor: Colors.blueAccent,
onPressed: () => _getACard(true),
child: Text(
"Reiniciar",
style: TextStyle(fontSize: 20.0),
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () =>_getACard(false),
tooltip: 'Next Card',
child: Icon(Icons.arrow_forward),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
You can copy paste run full code below
In your code, _cardsAux is _cards
You can in initState call _cardsAux = List.from(_cards);
code snippet
List<Map<String, String>> _cardsAux;
#override
void initState() {
_cardsAux = List.from(_cards);
working demo
full code
import 'package:flutter/material.dart';
import 'dart:math';
int random(min, max) {
var rn = new Random();
return min + rn.nextInt(max - min);
}
final List<Map<String, String>> _cards = [
{'card': 'Card A'},
{'card': 'Card B'},
{'card': 'Card C'}
];
var _card = 'Press arrow Icon below to Start';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Our Cards',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Our Cards'),
);
}
}
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 pop;
List<Map<String, String>> _cardsAux;
#override
void initState() {
_cardsAux = List.from(_cards);
super.initState();
}
void _getACard(bool isReset) {
setState(() {
if (isReset) {
_cardsAux.addAll(_cards);
}
if (_cardsAux.length == 0) {
_card = 'No More Cards';
} else {
pop = random(0, _cardsAux.length);
_card = _cardsAux[pop]['card'];
print(_card);
print("_cards ${_cards.length}");
print("_cardsAux ${_cardsAux.length}");
_cardsAux.removeAt(pop);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(30),
child: Text(
'$_card',
style: Theme.of(context).textTheme.headline3,
)),
Container(
padding: EdgeInsets.all(30),
child: FlatButton(
color: Colors.blue,
textColor: Colors.white,
padding: EdgeInsets.all(8.0),
splashColor: Colors.blueAccent,
onPressed: () => _getACard(true),
child: Text(
"Reiniciar",
style: TextStyle(fontSize: 20.0),
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _getACard(false),
tooltip: 'Next Card',
child: Icon(Icons.arrow_forward),
),
);
}
}
You can use ( Dart documentation: https://api.dartlang.org/stable/2.1.0/dart-core/Map/removeWhere.html )
Map.removeWhere((key, value) => toRemove.contains(key));

Flutter oddly not reloading widget

I have been working with flutter just a while now but I have never experienced such a weird problem. basically I'm checking if there is a logged on username show them the main page and if not show them the signup page. after the user signs up (and logs in at the same time) I want to take him to my main page. even though I return a new Scaffold the mobile screen doesn't change at all. not with a hot load or anything. but after stopping the program and running it again (because the user is logged in) it automatically goes to my main page (which I want to do without having to stop the program and running it again. any ideas why this is happening couldn't find anything related to this.
import 'package:sociagrowapp/models/user.dart';
import 'package:sociagrowapp/Authenticate/SignIn.dart';
import 'package:sociagrowapp/HomePages/Home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Wrapper extends StatefulWidget{
#override
createState() => _Wrapper();
}
class _Wrapper extends State<Wrapper> {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
print(user);
// return either the Home or Authenticate widget
if (user == null){
print('Should Changed 3');
return Scaffold(
body: SignIn()
);
}
else {
print('Should Changed');
print('Should Changed2');
return PageData()
}
}
}
Just in case I will add the PagesData Code but I don't think it is related to that.
import 'package:flutter/material.dart';
import 'package:sociagrowapp/services/auth.dart';
int selectedbotnavi = 0;
class DailyTasks extends StatefulWidget
{
#override
createState() => _DailyTasks();
}
class _DailyTasks extends State<DailyTasks>
{
Widget build(BuildContext context)
{
return Center(
child: Text("15")
);
}
}
class Settings extends StatefulWidget
{
#override
createState() => _Settings();
}
class _Settings extends State<Settings>
{
String _httpdataretrieved;
Widget build(BuildContext context)
{
return Column(
children: <Widget>[
Container(width:MediaQuery.of(context).size.width,
child: Text('Your Account Username',style: TextStyle(fontWeight: FontWeight.w400),),
alignment: Alignment.center,
padding: EdgeInsetsDirectional.fromSTEB(0, 20, 0, 0),
),
Container(width:MediaQuery.of(context).size.width,
child: Text(' Important: Your Account must be public for SociaGrow. \n There are limited Features available to private Accounts',style: TextStyle(fontWeight: FontWeight.w900,fontSize:14),
),
alignment: Alignment.center,
padding: EdgeInsetsDirectional.fromSTEB(0, 5, 0, 20),
),
Container(child: TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username',
),
),
width: MediaQuery.of(context).size.width * 0.8,
alignment: Alignment.center,
padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 15),
),
Container(
child: RaisedButton(
child: Text('Change Username'),
),
)
],
);
}
}
List <Widget> Pages = [new DailyTasks(),new DailyTasks(),new DailyTasks()];
class PageData extends StatefulWidget
{
#override
createState() => _PageData();
}
class _PageData extends State<PageData>
{
void _changeselectbotnaviindex(int index)
{
selectedbotnavi = index;
setState(() {
});
}
final AuthService _auth = AuthService();
#override
Widget build(BuildContext context)
{
return Scaffold(
appBar: AppBar(title: Container(
child: Image.asset('assets/Logo.png',width: 100,height: 200,),
padding: EdgeInsetsDirectional.fromSTEB(0, 10, 0 , 0),
),
actions: <Widget>[
FlatButton(
child: Text('Sign out'),
onPressed: () async {
await this._auth.signOut();
},
),
],
),
body: Pages[selectedbotnavi],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items :[
BottomNavigationBarItem(icon: Icon(Icons.timelapse),title:Text('Daily Tasks')),
BottomNavigationBarItem(icon: Icon(Icons.call_made),title:Text('Growth')),
BottomNavigationBarItem(icon: Icon(Icons.settings),title:Text('Settings')),],
currentIndex: selectedbotnavi,
onTap: _changeselectbotnaviindex,
selectedItemColor: Colors.amber[800],
unselectedItemColor: Colors.black,
showUnselectedLabels: true,
)
);
}
}
That is not the way you navigate to a new page in Flutter.
In Flutter, the way to navigate between pages is with Navigator, which is a widget that manages a set of child widgets with a stack discipline. That is, Navigator has everything ready for you to navigate between pages easily. When you create an app with MaterialApp, Flutter attaches a Navigator to the top of the widget tree for you under the hood. This way, you can access the Navigator in your widget tree via context, by calling Navigator.of(context).
In your case, when the user taps the sign up button in your sign up page, you should do something like:
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => PageData()));
This way, your app will navigate to PageData when the user signs in.
Check out this Flutter.dev article on navigating pages for more details on the topic.
You have to call SetState() so your build method is called again.
You could add a VoidListener to your SignIn(onLogin:(){setState(() {});})