I am not sure I understand exactly what you are doing from the code fragments above, but I think I understand the requirement:
- Menu1 has state
- SubMenu1 should be able to read and update the Menu1 state
- Menu1 invokes SubMenu1 as a "child" screen, there is no other path to SubMenu1
The simplest way to handle such simple state is for Menu1 to pass its state to SubMenu1 (as a constructor argument) when it displays it, and for SubMenu1 to update it directly. I have shown example code for this below. Of course, this is tight coupling between the two screens, but then it seems to be a tightly coupled requirement.
The Provider package is useful to separate the state from the UI. You can use this approach. You say "But suddenly, my form is no longer initialized each time Menu1 is called, but only once when the application starts". Surely Menu1 can make a call to a method to initialise the Provider when it is invoked? I have added example code for the Provider approach. Some brief notes:
- I use MultiProvider, even though I only have one ChangeNotifierProvider, this would make your code easier to read
- The ChangeNotifier FormData holds the form state
- The parent form initialises the form state
- Both forms call FormData methods to set and get the state
Long example code direct state follows:
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: FormTest(),
),
),
);
}
}
class FormTest extends StatefulWidget {
@override
_FormTestState createState() => _FormTestState();
}
class _FormTestState extends State<FormTest> {
int _selectedIndex = 0;
final List<Widget> _options = [
FormWidget(),
PlaceholderWidget(Colors.deepOrange),
PlaceholderWidget(Colors.green)
];
void _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example'),
backgroundColor: Colors.teal),
body: Center(
child: _options.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.teal),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: Colors.cyan),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.lightBlue,
),
],
type: BottomNavigationBarType.shifting,
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey,
iconSize: 40,
onTap: _onItemTap,
elevation: 5),
);
}
}
class PlaceholderWidget extends StatelessWidget {
final Color color;
PlaceholderWidget(this.color);
@override
Widget build(BuildContext context) {
return Container(
color: color,
);
}
}
class FormWidget extends StatefulWidget {
@override
FormWidgetState createState() {
return FormWidgetState();
}
}
class FormWidgetState extends State<FormWidget> {
final _formKey = GlobalKey<FormState>();
final _formData = {};
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
_formData['parent'] = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Parent text: ' + _formData['parent'])));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SubFormWidget(_formData)),
);
}
},
child: Text('SubForm'),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Subform text: ' + _formData['child'])));
}
},
child: Text('Submit'),
),
),
],
),
);
}
}
class SubFormWidget extends StatefulWidget {
final Map _formData;
SubFormWidget(this._formData);
@override
SubFormWidgetState createState() {
return SubFormWidgetState();
}
}
class SubFormWidgetState extends State<SubFormWidget> {
final _subFormKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _subFormKey,
child: Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example'),
backgroundColor: Colors.teal),
body: Column(
children: <Widget>[
Text(widget._formData['parent']),
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
widget._formData['child'] = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_subFormKey.currentState!.validate()) {
_subFormKey.currentState!.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Subform text: ' + widget._formData['child'])));
Navigator.pop(context);
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
Long example code for Provider approach:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<FormData>(
create: (ctx) => FormData(),
),
],
child: MaterialApp(
title: 'Test App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: FormTest2(),
),
);
}
}
class FormTest2 extends StatefulWidget {
FormTest2({Key key}) : super(key: key);
@override
_FormTest2State createState() => _FormTest2State();
}
class _FormTest2State extends State<FormTest2> {
int _selectedIndex = 0;
final List<Widget> _options = [
FormWidget2(),
PlaceholderWidget(Colors.deepOrange),
PlaceholderWidget(Colors.green)
];
void _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example 2'),
backgroundColor: Colors.teal),
body: Center(
child: _options.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.teal),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: Colors.cyan),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.lightBlue,
),
],
type: BottomNavigationBarType.shifting,
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey,
iconSize: 40,
onTap: _onItemTap,
elevation: 5),
);
}
}
class PlaceholderWidget extends StatelessWidget {
final Color color;
PlaceholderWidget(this.color);
@override
Widget build(BuildContext context) {
return Container(
color: color,
);
}
}
class FormWidget2 extends StatefulWidget {
@override
FormWidget2State createState() {
return FormWidget2State();
}
}
class FormData extends ChangeNotifier {
var _formData = <String, String>{};
void init() {
_formData = <String, String>{};
// No notifyListeners() needed in this use case
}
void setEntry(String key, String value) {
_formData[key] = value;
// notifyListeners() may or may not be needed in this use case
notifyListeners();
}
String getEntry(String key) {
var value = _formData[key];
return value;
}
}
class FormWidget2State extends State<FormWidget2> {
final _formKey = GlobalKey<FormState>();
@override
void initState() {
Provider.of<FormData>(context, listen: false).init();
super.initState();
}
@override
Widget build(BuildContext context) {
return Consumer<FormData>(
builder: (context, formData, _) => Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
initialValue: formData.getEntry('parent'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
formData.setEntry('parent', value);
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Parent text: ' + formData.getEntry('parent'))));
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SubFormWidget2()),
);
}
},
child: Text('SubForm'),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Subform text: ' + formData.getEntry('child'))));
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
class SubFormWidget2 extends StatefulWidget {
@override
SubFormWidget2State createState() {
return SubFormWidget2State();
}
}
class SubFormWidget2State extends State<SubFormWidget2> {
final _subFormKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _subFormKey,
child: Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example 2'),
backgroundColor: Colors.teal),
body: Consumer<FormData>(
builder: (context, formData, _) => Column(
children: <Widget>[
Text(formData.getEntry('parent')),
TextFormField(
initialValue: formData.getEntry('child'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
formData.setEntry('child', value);
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_subFormKey.currentState.validate()) {
_subFormKey.currentState.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Subform text: ' + formData.getEntry('child'))));
Navigator.pop(context);
}
},
child: Text('Submit'),
),
),
],
),
),
),
);
}
}