I was trying to use provider for managing the state of TextField Controller and below is the code for the provider I’m using. I want to know why is dispose called! not being printed out to the console when I leave forgot password page. I tested dispose method of stateful widget and it works fine as expected.
// Forgot password page provider
import 'package:flutter/material.dart';
class ForgotPasswordPageProvider with ChangeNotifier {
final TextEditingController _textEditingController = TextEditingController();
final _formKey = GlobalKey<FormState>();
TextEditingController get textEditingController => _textEditingController;
GlobalKey<FormState> get formKey => _formKey;
@override
void dispose() {
_textEditingController.dispose();
print("dispose called!");
super.dispose();
}
}
// Forgot password page
import 'package:chat_app/providers/forgot_password_page_provider.dart';
import 'package:chat_app/utils/form_validator.dart';
import 'package:chat_app/widgets/custom_app_bar.dart';
import 'package:chat_app/widgets/form_input_field.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
class ForgotPasswordPage extends StatelessWidget {
const ForgotPasswordPage({super.key});
@override
Widget build(BuildContext context) {
final provider = Provider.of<ForgotPasswordPageProvider>(context);
return Scaffold(
appBar: CustomAppBar(
leading: IconButton(
onPressed: () {
GoRouter.of(context).pop();
},
icon: const FaIcon(FontAwesomeIcons.xmark),
),
title: "Reset password",
),
body: Padding(
padding: EdgeInsets.symmetric(
horizontal: 24,
vertical: MediaQuery.of(context).size.height * 0.06),
child: Form(
key: provider.formKey,
child: Column(
children: [
Hero(
tag: "logo",
child: FaIcon(
FontAwesomeIcons.comments,
color: Theme.of(context).colorScheme.primary,
size: 96,
),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
FormInputField(
controller: provider.textEditingController,
hintText: "Enter your email address",
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.done,
validator: FormValidator.emailValidator,
label: "E-mail",
),
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
FilledButton(
onPressed: () {
if (provider.formKey.currentState!.validate()) {}
},
child: Text("Reset password"),
),
],
),
),
),
);
}
}
2
Answers
Try to
extend
ChangeNotifier instead of usingwith
modifier that is primarily used withmixins
to add more feature on existing class which we do not required in this particular scenario.this should work.
dispose
is not called because it is scoped to upper levels of your app. I can say that because you getting provider from context, meaning it is created somewhere in parent widgets, and not in-place.To scope provider to current page (ForgotPasswordPage), you should create provider in
build
method of that page. Something like:In that case, new instance of
ForgotPasswordPageProvider
will be created each time when this page created. Anddispose
will be called if this page is deleted from tree. Deleted is important, becausedispose
should be called whenProvider
is removed from tree, not just "invisible". That means, that if you use some routing, you should completely replace/delete route with that page to seedispose called!
, not just push some new page on top.If you still want to create
Provider
somewhere else in the app and calldispose
on current page, I do not think it is good idea. In that case, it will be hard to syncdispose
and recreation of provider and most probably you will get error, because you accessing your class afterdispose
called.