How to Maintain State of Drawer's Child Widget - flutter

I have a stateful widget, Counter, with a button and a counter that keeps track of how many times the button was pressed. This widget is in the drawer. When I close and open the drawer again, the counter is reset. How do I make it so that the counter is not reset upon closing and opening the drawer?
Here is the code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
drawer: Drawer(
child: Counter(),
),
appBar: AppBar(),
body: Container(),
),
);
}
}
class Counter extends StatefulWidget {
const Counter({super.key});
#override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(_count.toString()),
ElevatedButton(
onPressed: () {
setState(() {
_count = _count + 1;
});
},
child: Text('Increment Counter'),
)
],
);
}
}

To keep state of a variable within the Drawer(), the real solution would be to use a State Management library.
However, what you can do is create a global variable and pass it down the tree to Drawer():
import 'package:flutter/material.dart';
void main() {
runApp( MyApp());
}
class MyApp extends StatelessWidget {
var counter = 0;
MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
drawer: Drawer(
child: Counter(counter: counter,),
),
appBar: AppBar(),
body: Container(),
),
);
}
}
class Counter extends StatefulWidget {
int counter;
Counter({required this.counter,super.key});
#override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(widget.counter.toString()),
ElevatedButton(
onPressed: () {
setState(() {
widget.counter = widget.counter + 1;
});
},
child: Text('Increment Counter'),
)
],
);
}
}

Related

Stateless widget to stateful widget ( using Create state)

[error][1]
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
var questionIndex = 0;
void answerQuestion() {
setState(() {
questionIndex = questionIndex + 1;
});
print(questionIndex);
}
#override
Widget build(BuildContext context) {
var question = [
'what\'s your favorite colour?',
'what\'s your favorite sports?',
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My app'),
),
body: Column(
children: [
Text(question[questionIndex]),
RaisedButton(
child: Text('answer1'),
onPressed: () => print('answer2'),
),
RaisedButton(
child: Text('answer2'),
onPressed: () => print('answer2'),
),
RaisedButton(
child: Text('answer3'),
onPressed: () {
print('answer3');
}),
],
),
),
);
}
}
error
I'm on going course in Udemy in that they got the output for the same code but for me it showing error like this **
** Exception caught by widgets library
** MyAppState#f6d27(lifecycle state: created, no widget, not mounted)**
[1]: https://i.stack.imgur.com/NFGhN.jpg
You need to create a private class.
class FilmList extends StatefulWidget {
const FilmList({Key? key}) : super(key: key);
#override
_FilmListState createState() => _FilmListState();//need to add
}
class _FilmListState extends State<FilmList> {
MovieQuery query = MovieQuery.year;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
child:Container(),
);}
}

Dismiss keyboard when swiping between pages in PageView

I'm trying to dismiss the keyboard between swiping between screen2(has a textfield) and screen1 using the PageView widget. I've tried calling Focus.of(context).unfocus(); in the dispose method of screen 2.But the keyboard remains.. Here's a minimum example...
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter'),
),
body: PageView(
children: [
Screen1(),
Screen2(),
],
)),
);
}
}
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("Screen1");
}
}
class Screen2 extends StatefulWidget {
#override
_Screen2State createState() => _Screen2State();
}
class _Screen2State extends State<Screen2> {
#override
void dispose() {
Focus.of(context).unfocus();
super.dispose();
}
#override
Widget build(BuildContext context) {
return
Column(
children: [
Text("Screen2"),
TextField()
],
);
}
}
If you want to dismiss the keyboard when you're swiping on pages, you can use onPageChanged property, using WidgetsBinding.instance?.focusManager.primaryFocus?.unfocus() will be executed when the user scroll every page and there exist a focus.
I took your code and modified.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter'),
),
body: PageView(
onPageChanged: (index) {
WidgetsBinding.instance?.focusManager.primaryFocus?.unfocus();
},
children: [
Screen1(),
Screen2(),
],
)
),
);
}
}
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("Screen1");
}
}
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text("Screen2"),
TextField()
],
);
}
}

Why is my "Data"."data" not updated in ChangeNotifier class using ChangeNotifierProvider?

I am new to flutter.
I want to ask why when my text field's onChange did not trigger: "Provider.ofContext).updateData(newString);".
The value of my Provider.of(context).data is not updated and with the 2 print statements, only 'called1' is always printed out.
Here is the code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
create: (_) => Data(),
lazy: false,
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(Provider.of<Data>(context).data),
),
body: Level2(),
),
),
);
}
}
class Level2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
MyTextField(),
],
);
}
}
class MyTextField extends StatelessWidget {
#override
Widget build(BuildContext context) {
return TextField(onChanged: (newString) {
print('called1');
Provider.of<Data>(context).updateData(newString);
print('called2');
});
}
}
class Data extends ChangeNotifier {
String data = '1234567890';
void updateData(newString) {
data = newString;
notifyListeners();
}
}
You are trying to access provider in same widget where you are declaring, which is not right way to do, provider must declare in above widget where you are accessing.
Moreover always use provider data by variable(as used in MyTextField widget) other wise it will not work.
Following code may help you to understand more.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
create: (_) => Data(),
child: MaterialApp(home: Level1()),
);
}
}
class Level1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Provider.of<Data>(context).data),
),
body: Level2(),
);
}
}
class Level2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
MyTextField(),
],
);
}
}
class MyTextField extends StatelessWidget {
var dataprovider;
#override
Widget build(BuildContext context) {
dataprovider = Provider.of<Data>(context);
return TextField(
onChanged: (newString) {
print(dataprovider.data);
dataprovider.updateData(newString);
print('called2');
},
);
}
}
class Data extends ChangeNotifier {
String data = '1234567890';
void updateData(newString) {
print("cds");
data = newString;
notifyListeners();
}
}

how to edit value at the child widget

i try to edit the value of the child widget, i can do it with StatefulWidget parent but i want to do it with StatelessWidget parent and without using global value
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(child: Text('addFile'), onPressed: () {}),
FlatButton(child: Text('deleteFile'), onPressed: () {})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
Widget build(BuildContext context) {
return hasFile ? Text('has a file') : Text("no File");
}
}
You are thinking the wrong way. Child aka Text() should get its value from a model which is managed by the application or at least managed by the widget above. I would go with the provider package https://pub.dev/packages/provider and do this:
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
class MyState with ChangeNotifier {
String _myText;
MyState(this._myText);
getMyText() => _myText;
void changeText(String newText) {
_myText = newText;
notifyListeners();
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => MyState("initial Text")),
],
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("addFile");
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("deleteFile");
})
],
),
body: Child(),
));
}
}
class Child extends StatelessWidget {
#override
Widget build(BuildContext context) {
MyState myState = Provider.of<MyState>(context);
return Text(myState.getMyText());
}
}
This is coded without IDE support or even compiling and running. But it should get you to the right direction.
You can use BLoC pattern to implement this kind of functionality,
Here is the BLoC class which will handle state of bool
import 'dart:async';
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();
Then you can add stream builder in your Stateful Widget, in which you will provide stream of BLoC class.
StreamBuilder updates it's UI according to Stream.
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
At last you can access BLoC class with your stateless widget as follows
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
Full example is as below
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Homepage(),
);
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();

How can I move forward and backward data between to different stateful widgets in Flutter?

There is count variable in the first stateful widget, I have passed it to Setting class. And, Setting class passes it toSettingStateBuilder. Then, its value is changed in incrementing() in SettingStateBuilder. I want the updated value to return back to HomePageBody for further work. How can I do that?
The first stateful widget is created as follow:
class HomePage extends StatefulWidget {
#override
HomePageBody createState() => HomePageBody();
}
class HomePageBody extends State<HomePage> {
int count=0;
#override
Widget build(BuildContext context) {
...
new Setting(count);
}
}
The second stateful widget is created as follow:
class Setting extends StatefulWidget {
int count;
Setting(this.count);
#override
SettingStateBuilder createState() => SettingStateBuilder(count);
}
class SettingStateBuilder extends State<Setting> {
int count;
SettingStateBuilder(this.count);
#override
Widget build(BuildContext context) {
return new Container(
new Text(count.toString());
....
onPressed: () => setState(() => incrementing(context))),
);
}
incrementing(context) { count += 1; }
}
You could add a Function property to the Settings widget that will be called when the counter is incremented, and pass that function when you create the widget so in HomePage you can update the counter:
class HomePage extends StatefulWidget {
#override
HomePageBody createState() => HomePageBody();
}
class HomePageBody extends State<HomePage> {
int count = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
MaterialButton(
child: Text('Settings'),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => Settings(
count,
(newCount) {
setState(
() {
count = newCount;
},
);
},
),
),
);
},
)
],
),
body: Center(
child: Text('$count'),
),
);
}
}
class Settings extends StatefulWidget {
final int count;
final Function(int) onCounterChanged;
Settings(this.count, onCounterChanged);
#override
SettingsStateBuilder createState() => SettingsStateBuilder(count);
}
class SettingsStateBuilder extends State<Settings> {
int count;
SettingsStateBuilder(this.count);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$count'),
MaterialButton(
child: Text('Increment'),
onPressed: () => setState(
() {
increment();
},
),
),
],
),
),
);
}
increment() {
count += 1;
widget.onCounterChanged(count);
}
}
If you are dealing with a more complex use case I suggest you to read for how to approach state management in Flutter, some resources:
https://flutter.dev/docs/development/data-and-backend/state-mgmt/intro
https://flutter.dev/docs/development/data-and-backend/state-mgmt/options