Я пытаюсь динамически отображать виджеты в зависимости от состояния пустоты/непустоты виджета TextField. Ниже приведен код, который я написал. Но код не создает виджет динамически, даже когда я ввожу текст в поле TextField для процентной ставки налога. Это неожиданно. Здесь я также не уверен, можем ли мы назначить Provider с помощью late
, как я сделал в коде.
Что я пробовал. Я пытался объявить логическую переменную isEmptyTaxPercentage
в классе Provider как static
и установить ее с помощью статического метода доступа в onChanged поля TextField для процентной ставки налога. Но, поскольку функция notifyListeners() не была вызвана, возможно, это тоже не сработало так, как ожидалось: предполагаемый виджет не появился динамически.
Код:
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("Стоимость:"),
),
Expanded(
child: TextField(
controller: costController,
),
),
],
),
Row(
children: [
const Expanded(
child: Text("Процент налога (%):"),
),
Expanded(
child: TextField(
controller: taxPercentageController,
onChanged: (value) {
if (value.isEmpty) {
myTaxProvider.isEmptyTaxPercentage = true;
} else {
myTaxProvider.isEmptyTaxPercentage = false;
}
},
),
),
],
),
Consumer<MyTaxProvider>(
builder: (context, provider, child) {
if (provider.isEmptyTaxPercentage) {
return Container();
} else {
return Row(
children: [
const Expanded(
child: Text("Сумма налога:"),
),
Expanded(
child: TextField(
controller: taxAmountController,
),
),
],
);
}
},
),
const Text("Другие виджеты."),
],
),
),
);
}
}
Ответ
Проблема, с которой вы столкнулись, заключается в том, что изменения состояния переменной isEmptyTaxPercentage
в вашем классе MyTaxProvider
не обновляют виджеты в нижней части вашего интерфейса. Это происходит из-за того, что вы не вызываете метод notifyListeners()
после изменения состояния переменной.
Чтобы исправить это, необходимо изменить метод onChanged
текстового поля для taxPercentageController
, чтобы он вызывал метод, который изменяет состояние и уведомляет слушателей.
Вот исправленный код:
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 percentage (%):"),
),
Expanded(
child: TextField(
controller: taxPercentageController,
onChanged: (value) {
myTaxProvider.setTaxPercentageEmptynessState(value.isEmpty);
},
),
),
],
),
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)."),
],
),
),
);
}
}
Объяснение изменений:
-
Использование метода изменения состояния: Теперь вместо прямого изменения переменной
isEmptyTaxPercentage
вonChanged
вы вызываете методsetTaxPercentageEmptynessState()
, что позволяет обновить состояние и вызватьnotifyListeners()
. Это уведомляет все слушатели о том, что состояние изменилось, и они могут обновить свои виджеты. - Убраны
static
переменные: Так какisEmptyTaxPercentage
больше не является статической переменной, это позволяет избежать проблем с состоянием, которые могут возникнуть, если разные экземпляры классов работают с одним и тем же статическим значением.
Теперь ваш интерфейс будет обновляться правильно в зависимости от ввода в текстовом поле.