Не удается динамически отобразить виджет в Flutter с использованием Provider?

Вопросы и ответы

Я пытаюсь динамически отображать виджеты в зависимости от состояния пустоты/непустоты виджета 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)."),
          ],
        ),
      ),
    );
  }
}

Объяснение изменений:

  1. Использование метода изменения состояния: Теперь вместо прямого изменения переменной isEmptyTaxPercentage в onChanged вы вызываете метод setTaxPercentageEmptynessState(), что позволяет обновить состояние и вызвать notifyListeners(). Это уведомляет все слушатели о том, что состояние изменилось, и они могут обновить свои виджеты.

  2. Убраны static переменные: Так как isEmptyTaxPercentage больше не является статической переменной, это позволяет избежать проблем с состоянием, которые могут возникнуть, если разные экземпляры классов работают с одним и тем же статическим значением.

Теперь ваш интерфейс будет обновляться правильно в зависимости от ввода в текстовом поле.

Оцените материал
Добавить комментарий

Капча загружается...