I am building a Flutter application that includes an extensive form for Technician registration. The form is split across multiple pages using a PageView widget, with each page containing several TextField widgets. Some of these fields are mandatory and others optional. Each page has its own set of TextEditingControllers.
I am using the provider package for state management. I want the controllers to be created anew each time I navigate into the form view, ensuring a fresh start each time while still managing state efficiently.
How can I achieve this? Below is the structure of my current implementation and the desired behavior:
Registration Page
class RegistrationPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Technician Registration')),
body: Column(
children: [
Expanded(
child: PageView(
children: [
Page1(),
Page2(),
],
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => AnotherPage(
controller1: Page1.page1Controller1,
controller2: Page1.page1Controller2,
controller3: Page2.page2Controller1,
controller4: Page2.page2Controller2,
),
),
);
},
child: Text('Go to Another Page'),
),
],
),
);
}
}
Page 1 and 2
class Page1 extends StatelessWidget {
static final page1Controller1 = TextEditingController();
static final page1Controller2 = TextEditingController();
const Page1({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(controller: page1Controller1),
TextField(controller: page1Controller2),
],
);
}
}
class Page2 extends StatelessWidget {
static final page2Controller1 = TextEditingController();
static final page2Controller2 = TextEditingController();
const Page2({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(controller: page2Controller1),
TextField(controller: page2Controller2),
],
);
}
}
I tried to keep the form data intact as I navigate between pages, I initially used static controllers. However, I faced issues when disposing of these controllers and reusing them after navigating to the form view, resulting in exceptions about disposed controllers being used.
I expect that
I am using provider for state management, I need to know the best way to achieve my desired result without getting bugged by annoying exceptions.
First thing first, you should never declare the controllers as static.
Solution
import 'package:flutter/material.dart';
class RegistrationFormState extends ChangeNotifier {
final TextEditingController page1Controller1 = TextEditingController();
final TextEditingController page1Controller2 = TextEditingController();
final TextEditingController page2Controller1 = TextEditingController();
final TextEditingController page2Controller2 = TextEditingController();
// Dispose all controllers
void disposeControllers() {
page1Controller1.dispose();
page1Controller2.dispose();
page2Controller1.dispose();
page2Controller2.dispose();
}
@override
void dispose() {
disposeControllers();
super.dispose();
}
}
RegistrationPage
:import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class RegistrationPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => RegistrationFormState(),
child: Scaffold(
appBar: AppBar(title: Text('Technician Registration')),
body: Column(
children: [
Expanded(
child: PageView(
children: [
Page1(),
Page2(),
],
),
),
ElevatedButton(
onPressed: () {
final formState = Provider.of<RegistrationFormState>(context, listen: false);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => AnotherPage(
controller1: formState.page1Controller1,
controller2: formState.page1Controller2,
controller3: formState.page2Controller1,
controller4: formState.page2Controller2,
),
),
);
},
child: Text('Go to Another Page'),
),
],
),
),
);
}
}
RegistrationFormState
in Page1 and Page2:import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Page1 extends StatelessWidget {
const Page1({super.key});
@override
Widget build(BuildContext context) {
final formState = Provider.of<RegistrationFormState>(context);
return Column(
children: [
TextField(controller: formState.page1Controller1),
TextField(controller: formState.page1Controller2),
],
);
}
}
class Page2 extends StatelessWidget {
const Page2({super.key});
@override
Widget build(BuildContext context) {
final formState = Provider.of<RegistrationFormState>(context);
return Column(
children: [
TextField(controller: formState.page2Controller1),
TextField(controller: formState.page2Controller2),
],
);
}
}
With this setup, every time you navigate to the RegistrationPage
, a new instance of RegistrationFormState
is created, providing fresh TextEditingController
instances. These controllers are disposed of when the RegistrationPage
is popped off the navigation stack, ensuring that no disposed controllers are reused.
Note: I'm not sure why are you using passing the controllers to AnotherPage
. But if you just wanna pass the input values you should pass it as:
AnotherPage(
controller1Text: formState.page1Controller1.text,
controller2Text: formState.page1Controller2.text,
controller3Text: formState.page2Controller1.text,
controller4Text: formState.page2Controller2.text,
)