Flutter - Persistent State between Pages - flutter

I'm trying to store some state during page changes. So old data values are available when a page is reloaded.
I've been looking into PageStorage but haven't figured out how it works yet.
I'm writing into the storage with PageStorage.of(context)?.writeState(context, 'Some text is here', identifier: ValueKey('test')); and then unloading the page with the back button.
When I reload the page (with Navigator.of(context).push()), using PageStorage.of(context)?.readState(context, identifier: ValueKey('test')); just gives me null;
Here's a short sample that I wrote to demonstrate how I'm using it. Does anyone know what I'm doing wrong?
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new NewPage();
}));
},
),
);
}
}
class NewPage extends StatefulWidget {
NewPageState createState() => NewPageState();
}
class NewPageState extends State<NewPage> {
String _text = '';
#override
void initState() {
super.initState();
_text = PageStorage
.of(context)
?.readState(context, identifier: ValueKey('test'));
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
Text('The text is $_text'),
FloatingActionButton(
onPressed: () {
setState(() {
PageStorage.of(context)?.writeState(
context, 'Some text is here',
identifier: ValueKey('test'));
});
},
)
],
),
);
}
}

There were multiple issues with the code you provided.
The first one being in your MyAppState where you didn't provided a key to your PageStorage. Indeed without the key , the written data cannot be saved and I quote :
writeState(BuildContext context, dynamic data, {Object identifier}) → void
package:flutter
Write the given data into this page storage bucket using the specified identifier or an identifier computed from the given context. The computed identifier is based on the PageStorageKeys found in the path from context to the PageStorage widget that owns this page storage bucket.
If an explicit identifier is not provided and no PageStorageKeys are found, then the data is not saved.
To resolve this just create a global variable PageStorageKey mykey = new PageStorageKey("testkey"); and pass it along the creation of your PageStorage:
class MyAppState extends State<MyApp> {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
key: mykey,
),
);
}
}
Then use the same key again to write the data :
onPressed: () {
setState(() {
PageStorage.of(context).writeState(context, 'Data saved',
identifier: ValueKey(mykey));
updateText();
});
Finally the way you update the text is, in my opinion not the best way to do it.
You should create a method (updateText() for example) and call it after you wrote your data.
updateText() {
if (PageStorage.of(context) .readState(context, identifier: ValueKey(mykey)) != null) {
_text = PageStorage .of(context).readState(context, identifier: ValueKey(mykey));
}
else {
_text = 'PageStorageNull';
}
}
As always it's safer to check if the value is non-null to avoid errors.
Here is the full code :
void main() => runApp(new MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
key: mykey,
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(context) {
return Center(
child: FloatingActionButton(
onPressed: () {
Navigator.push(context,
new MaterialPageRoute(builder: (context) => new NewPage()));
},
),
);
}
}
class NewPage extends StatefulWidget {
NewPageState createState() => NewPageState();
}
class NewPageState extends State<NewPage> {
String _text;
#override
void initState() {
super.initState();
}
updateText() {
if (PageStorage.of(context) .readState(context, identifier: ValueKey(mykey)) != null) {
_text = PageStorage .of(context).readState(context, identifier: ValueKey(mykey));
}
else {
_text = 'PageStorageNull';
}
}
#override
Widget build(context) {
return Center(
child: Column(
children: <Widget>[
Text('The text is $_text'),
FloatingActionButton(
onPressed: () {
setState(() {
PageStorage.of(context).writeState(context, 'Data saved',
identifier: ValueKey(mykey));
updateText();
});
},
)
],
),
);
}
}
With this code, press the button to go to the second page. On the second page press the button to update the text with the data provided in the writeState() method.
Hoping this can help you,
Regards
EDIT
Fist things first, sorry for misunderstanding the point.
And actually what you want is possible by using Buckets.
Indeed the : PageStorage .of(context).readState(context, identifier: ValueKey(mykey)); can be replace by :
_bucket.readState(context, identifier: ValueKey(mykey));
So what you have to do is make your _bucket variable global, then you need to wrap everything you have in your NewPageState within a PageStorage using the same Key and Bucket as your first PageStorage in the MyAppState
Doing so you will be able to read using the bucket too and keep your data through navigation.
Again he is the full code:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
final PageStorageBucket _bucket = new PageStorageBucket();
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(context) {
return new MaterialApp(
home: PageStorage(
child: new MyHomePage(),
bucket: _bucket,
key: mykey,
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(context) {
return Center(
child: FloatingActionButton(
onPressed: () {
Navigator.push(context,
new MaterialPageRoute(builder: (context) => new NewPage()));
},
),
);
}
}
class NewPage extends StatefulWidget {
NewPageState createState() => NewPageState();
}
class NewPageState extends State<NewPage> {
String _text;
#override
void initState() {
super.initState();
updateText();
}
updateText() {
if (_bucket.readState(context, identifier: ValueKey(mykey)) != null) {
_text = _bucket.readState(context, identifier: ValueKey(mykey));
}
else {
print(_bucket.toString());
}
}
#override
Widget build(context) {
return PageStorage(
key:mykey,
bucket: _bucket,
child: Column(
children: <Widget>[
Text('The text is $_text'),
FloatingActionButton(
onPressed: () {
setState(() {
_bucket.writeState(context, 'Data saved',
identifier: ValueKey(mykey));
updateText();
});
},
)
],
),
);
}
}

Related

Update variable outside a widget in Flutter?

Is it possible to update a variable outside a widget while calling it ?
Here's an example :
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
#override
Widget build(BuildContext context) {
return Column(children: [
Text(example),
Widget2(example: example)
],);
}
}
class Widget2 extends StatefulWidget {
final String example;
Widget2({required this.example});
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() {
widget.example = 'B'
}),
child: Text('update !'),
);
}
}
The idea here is that I want to update example using a button outside the widget.
This code is not working : example = 'A' no matter if I click the button or not, but I don't understand why since I'm calling the same variable.
Is there a simple solution to achieve this ? (by simple, I mean without the need of Provider or else.)
You can use callback method. Parent widget needed to updated, so setState is needed to be trigger on Widget1.
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(example),
Widget2(
example: example,
callback: (p0) {
setState(() {
example = p0;
});
},
),
],
);
}
}
class Widget2 extends StatefulWidget {
final String example;
final Function(String) callback;
Widget2({
required this.example,
required this.callback,
});
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
widget.callback("new data");
},
child: Text('update !'),
);
}
}
You can use Notifiers, here is an example:
import 'package:flutter/material.dart';
class ExampleNotifier with ChangeNotifier {
String example = 'A';
ExampleNotifier();
setText(string x) {
example = x;
notifyListeners();
}
}
and then use it like:
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
var exampleNotifier = Provider.of<ExampleNotifier>(context);
return Column(
children: [
Text(exampleNotifier.example),
Widget2(),
],
);
}
}
class Widget2 extends StatefulWidget {
#override
State<Widget2> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
var exampleNotifier = Provider.of<ExampleNotifier>(context, listen: false);
return ElevatedButton(
onPressed: () {
exampleNotifier.setText('B');
},
child: Text('update !'),
);
}
}
If you want to use setState, you can use this
class Widget1 extends StatefulWidget {
#override
State<Widget1> createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
String example = 'A';
void changeExample() {
setState(() => example = "B");
}
#override
Widget build(BuildContext context) {
return Column(
children: [Text(example), Widget2(changeExample: changeExample)],
);
}
}
class Widget2 extends StatelessWidget {
final void Function() changeExample;
Widget2({required this.changeExample});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: changeExample,
child: Text('update !'),
);
}
}

While learning flutter using (https://github.com/afitz0/exploration_planner). How to implement the action on the LinearProgressIndicator()?

This code is part of online training of flutter by Google team. The original code can be accessed in https://github.com/afitz0/exploration_planner. I am new on flutter and I´ve got some dificulties to use statefull widget. I still do not have enough confidence. I made some modification on original code to add action to the indicator bar, it works fine but I dont think my solution is ideal...
My question is related to the right way to make a change in the state of the taskitem give an
update on the linearProgressIndicator ? Thanks in advance..
import 'package:flutter/material.dart';
double _percentual = 0; //variable to hold progress bar values from zero to 1 step 0.2
// first comes root run appp
void main() => runApp(MyApp()
//MaterialApp
//Scaffold
//AppBar
//Text
//body: Column
//text, text, text
//image
//Row
//text, text, bttom
//....
);
// second comes materialapp
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Exploration!',
theme: ThemeData(primarySwatch: Colors.blueGrey),
home: MyHomePage(),
);
}
}
//third comes home page describes visual of app
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
late AnimationController controller;
#override
void initState() {
controller = AnimationController(
vsync: this,
)..addListener(() {
setState(() {
controller.value = _percentual;
});
});
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Space Exploration planner'),
),
body: Column(
children: [
Progress(),
TaskList(),
],
),
);
}
}
class Progress extends StatefulWidget {
const Progress({super.key});
#override
State<Progress> createState() => _ProgressState();
}
class _ProgressState extends State<Progress> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text('You are this far away from exploring the whole universe'),
LinearProgressIndicator(
value: _percentual,
)
],
);
}
}
class TaskList extends StatelessWidget {
const TaskList({super.key});
#override
Widget build(BuildContext context) {
return Column(
children: [
TaskItem(label: "Load rocket with supplies"),
TaskItem(label: "Launch rocket"),
TaskItem(label: "Circle the home planet"),
TaskItem(label: "Head out to de first moon"),
TaskItem(label: "Launch moon lander #1"),
],
);
}
}
class TaskItem extends StatefulWidget {
final String label;
const TaskItem({Key? key, required this.label}) : super(key: key);
#override
State<TaskItem> createState() => _TaskItemState();
}
class _TaskItemState extends State<TaskItem> {
bool? _value = false;
#override
Widget build(BuildContext context) {
return Row(
children: [
Checkbox(
onChanged: (newValue) => setState(() => {
_value = newValue,
if (_value == true)
{
_percentual = double.parse(
(_percentual + 0.2).toStringAsPrecision(1)),
_ProgressState(),
}
else if (_value == false)
{
_percentual = double.parse(
(_percentual - 0.2).toStringAsPrecision(1)),
_ProgressState(),
},
main(), *//<-- worked like hot-reload but I dont think is the right way to do it.*
}),
value: _value,
),
Text(widget.label),
],
);
}
}

How to redraw StatefulWidget

On the example below, since MyStatefulWidget has a state, it doesn't matter if setState is called on _MyAppState, because it will not be redrawn.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){setState(() { value+=1; });},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
If I used a StatelessWidget it'd be redrawn, but this is just an example, there are cases where I need to redraw a StatefulWidget when setState is called.
One option would be to give it a name and build it from the setState, but I need it to be draw in the place where it's draw right now.
Dartpad: https://dartpad.dev/968be8755d5deab1ca5c8c84a993eafc
You could directly use widget.title in the Text widget to update the counter on screen. Please see the code below :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
void changeVal(int val) {
setState(() {
value = val;
});
}
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('App Example')),
body: Row(children: [
MyStatefulWidget(
title: value.toString(),
groupValue: value % 10,
chnageVal: changeVal),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: const Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key, this.title, this.groupValue, this.chnageVal})
: super(key: key);
final String title;
final int groupValue;
final Function(int) chnageVal;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
//String title;
// #override
// void initState() {
// super.initState();
// if (widget.title!=null) {
// title = widget.title;
// } else {
// title = "";
// }
// }
//int value = 0;
List<int> numbers = List.generate(10, (index) => index);
#override
Widget build(BuildContext context) {
return Container(
width: 120,
child: Column(children: [
Text(widget.title),
...numbers
.map((number) => RadioListTile<int>(
title: Text('$number'),
value: number,
groupValue: widget.groupValue,
onChanged: (val) {
widget.chnageVal(val);
},
))
.toList()
]),
);
}
}
Just provide a unique key while calling MyStatefulWidget like MyStatefulWidget(key: UniqueKey(), title: value.toString()),.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children: [
MyStatefulWidget(key: UniqueKey(), title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}) : super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title != null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
To know more about key please go through this article.
I will recommend using Stream, better performance and not so hard to use for refresh partial UI.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
StreamController<int> _streamController = StreamController();
int value = 0;
#override
void dispose() {
_streamController.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home:
StreamBuilder<int>(
stream: _streamController.stream,
initialData: value,
builder:
(BuildContext context, AsyncSnapshot<int> snapshot) {
return Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){_streamController.sink.add(value++);},
child: new Text("Add"),
)
]),
);},),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}

Flutter: Update TextFormField text with ChangeNotifier

In a complex scenario I need to update the text of some TextFormFields when a notifyListeners() is sent by a Model extending ChangeNotifier.
The problem is that to change the text of a TextFormField you have to use the setter TextFormField.text which implies a rebuild, and so you can't use it into the build method. But to access the Provider of the model you need the context which is inside the build method.
MWE (obviously the button is in another Widget in the real project, and there are more TextFormFields)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyModel extends ChangeNotifier {
void updateCounter() {
++_counter;
notifyListeners();
}
MyModel() {
_counter = 1;
}
int _counter;
String get counter => _counter.toString();
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyModel(),
child: MaterialApp(
title: 'Test',
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _text1Ctl = TextEditingController();
var _text2Ctl = TextEditingController();
#override
void initState() {
super.initState();
final model = MyModel();
model.addListener(() {
_text1Ctl.text = model.counter;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
FlatButton(
onPressed: () {
Provider.of<MyModel>(context, listen: false).updateCounter();
},
child: Text('Press me'),
),
// 1st attempt
// Doesn't work because the listener isn't applied to the instance of the model provided by the provider.
TextFormField(controller: _text1Ctl),
// 2nd attempt
// Works but with `Another exception was thrown: setState() or markNeedsBuild() called during build.` because it changes text via controller (which implies a rebuild) during building.
Consumer<MyModel>(builder: (context, model, child) {
_text2Ctl.text = model.counter;
return TextFormField(controller: _text2Ctl);
})
]));
}
}
Your second example works without any errors when I run it:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyModel extends ChangeNotifier {
void updateCounter() {
++_counter;
notifyListeners();
}
MyModel() {
_counter = 1;
}
int _counter;
String get counter => _counter.toString();
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyModel(),
child: MaterialApp(
title: 'Test',
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _text2Ctl = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
FlatButton(
onPressed: () {
Provider.of<MyModel>(context, listen: false).updateCounter();
},
child: Text('Press me'),
),
// 2nd attempt
// Works but with `Another exception was thrown: setState() or markNeedsBuild() called during build.` because it changes text via controller (which implies a rebuild) during building.
Consumer<MyModel>(builder: (context, model, child) {
_text2Ctl.text = model.counter;
return TextFormField(controller: _text2Ctl);
})
]));
}
}

How to call a method in another stateful widget

I am building a podcasting type app, so need to call the record, stop, and play functions in many places, I created the methods, but difficulty to call these methods in other places.
main.dart
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String statusText = "";
bool isComplete = false;
void startRecord() //Need to call all of these method in coming stateful widgets
void stopRecord() //
void pauseRecord()//
void resumeRecord()//
void play() //
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => Scaffold(
drawer: Drawer(
elevation: 2.0,
child: ListView(
children: <Widget>[
ListTile(
title: Text('Home'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return MyApp();
},
),
);
},
),
//more code is here
Expanded(
child: GestureDetector(
child: IconButton(
icon: Icon(Icons.mic),
color: Colors.white,
iconSize: 40,
onPressed: () async {
startRecord();
}),
),
),
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
onPressed: () {
startRecord()
// need to call the method here.
}
Pressed: () {
stopRecord()
// need to call the method here.
}
Pressed: () {
play()
// need to call the method here.
}
),
}
Need to call all the methods from a first stateful widget for bottom stateful widgets
also, need to call these methods for other classes when code progress
both stateful widgets are in the main.dart. I could not call the method from the first class for the second stateful widget
This is not a rocket science, just a simple line of code, and you are done.
What you have to do, is to just call the MyHomePage() and let it accept the startRecording() to be used inside the Widget
1. Passing the data from MyApp() to MyHomePage()
#override
Widget build(BuildContext context) {
return MaterialApp(
// here you pass the your function
home: MyHomePage(onPressed: startRecording)
);
}
2. Receiving the data in MyHomePage()
class MyHomePage extends StatefulWidget {
// let it accept a function type onPressed argument
final Function onPressed;
// constructor
MyHomePage({Key key, this.onPressed}): super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
// simply call the onPressed which received your startRecording() from MyApp
onPressed: () => widget.onPressed()
}
You can get the state of a parent widget using the BuildContext of the child widget like so:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
static _MyAppState of(BuildContext context) {
return context.findAncestorStateOfType<_MyAppState>();
}
}
class _MyAppState extends State<MyApp> {
String statusText = "";
bool isComplete = false;
void startRecord() {
print('Hello');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
MyApp.of(context).startRecord();
return Scaffold(
body: Placeholder(),
);
}
}
Simply define that function outside the class as a stand-alone function like this But if you want to call from inside the class. Heres the code.
inside a different class as a static function:
onPressed: () {
_MyAppState().startRecord(); //call using the class name.
}
Like this inside your onpressed Statement.
Should work.
Or else what you can do is define the function outside the class. Then use it where ever you want. Like this:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
void startRecord(){
.
.
.
} /// Like here outside the class
class _MyAppState extends State<MyApp> {
String statusText = "";
bool isComplete = false;
#override
Widget build(BuildContext context) {
return MaterialApp(.....
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
onPressed: () {
startRecord(); // call Here as follows.
}),
}