I am trying to display widget(s) dynamically based on the empty/non-empty state of a TextField widget. Below is the code I’ve written. But the code does not dynamically create the widget even when I type in text in the TextField of Tax Percentage. This is unexpected. Here, I am also not sure if we can late
assign the Provider the way I have done in the code.
What I’ve tried. I’ve tried declaring the boolean variable isEmptyTaxPercentage
in the Provider class as static
and set using static access method in the onChanged of the TaxPercentage TextField. But, as no notifyListeners() function was called, perhaps, this also did not work as expected: the intended widget didn’t show up dynamically.
Update 01: Fixed the code below. Now it works as intended. The only question remaining is declaration of the provider as late MyTaxProvider myTaxProvider
in the State class is valid and good practice or not?
The code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<MyTaxProvider>(
create: (context) => MyTaxProvider(),
),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Provider Demo'),
);
}
}
class MyTaxProvider with ChangeNotifier {
bool isEmptyTaxPercentage = true;
void setTaxPercentageEmptynessState(bool value) {
isEmptyTaxPercentage = value;
notifyListeners();
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final TextEditingController costController;
late final TextEditingController taxPercentageController;
late final TextEditingController taxAmountController;
late MyTaxProvider myTaxProvider;
@override
void initState() {
super.initState();
costController = TextEditingController();
taxPercentageController = TextEditingController();
taxAmountController = TextEditingController();
myTaxProvider = Provider.of<MyTaxProvider>(context, listen: false);
}
@override
void dispose() {
costController.dispose();
taxPercentageController.dispose();
taxAmountController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
children: [
Row(
children: [
const Expanded(
child: Text("Cost:"),
),
Expanded(
child: TextField(
controller: costController,
),
),
],
),
Row(
children: [
const Expanded(
child: Text("Tax precentage (%):"),
),
Expanded(
child: TextField(
controller: taxPercentageController,
onChanged: (value) {
if (value.isEmpty) {
myTaxProvider.setTaxPercentageEmptynessState(true);
} else {
myTaxProvider.setTaxPercentageEmptynessState(false);
}
},
),
),
],
),
Consumer<MyTaxProvider>(
builder: (context, provider, child) {
if (provider.isEmptyTaxPercentage) {
return Container();
} else {
return Row(
children: [
const Expanded(
child: Text("Tax Amount:"),
),
Expanded(
child: TextField(
controller: taxAmountController,
),
),
],
);
}
},
),
const Text("Other widget(s)."),
],
),
),
);
}
}
Declaring the provider as late is not recommended you should probably instead use it like below inside your build method
final myTaxProvider = Provider.of<MyTaxProvider>(context, listen: false);
That way your code is more clear and avoid potential errors regarding late initialization.
5