моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Оглавление
1. Важность локального обновления
2. Несколько способов реализовать частичное обновление
1. Используйте метод setState для частичного обновления.
2. Используйте StatefulWidget и InheritedWidget, чтобы частично обновить пользовательский интерфейс.
3.ValueNotifier и ValueListenableBuilder
7. Используйте глобальный ключ
Во Flutter управление состоянием означает управление и обновление состояния данных в приложении, а также обновление пользовательского интерфейса в соответствии с изменениями состояния. Эффективное управление состоянием может помочь разработчикам создавать более эффективные и удобные в обслуживании приложения.
setState — это самый простой метод управления состоянием во Flutter. При изменении состояния платформа будет уведомлена о необходимости перестроить пользовательский интерфейс. Конечно, мы знаем, что когда мы вызываем метод setState, страница будет перерисована. Когда макет страницы более сложный, иногда нам нужно обновить только отдельный пользовательский интерфейс. В настоящее время, если мы используем метод setState, он будет иметь. более высокая производительность. Используйте для перерисовки пользовательского интерфейса текущей страницы.
Итак, какие методы существуют во Flutter для частичного обновления пользовательского интерфейса. В этом блоге перечислено несколько способов реализации частичного обновления во Flutter.
Частичное обновление означает обновление только части интерфейса, а не всей страницы. Это улучшает производительность и удобство использования.
setState — наиболее часто используемый метод управления состоянием во Flutter. Он используется для уведомления платформы об изменениях состояния, что приводит к реконструкции интерфейса.
Когда мы создаем проект Flutter, примером таймера, генерируемого системой по умолчанию, является пример частичного обновления setState.
- import 'package:flutter/material.dart';
-
- class SetStateMainPage extends StatefulWidget {
- final String title;
- const SetStateMainPage({super.key, required this.title});
-
- @override
- State<SetStateMainPage> createState() => _SetStateMainPageMainPageState();
- }
-
- class _SetStateMainPageMainPageState extends State<SetStateMainPage> {
- int _count = 0;
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(widget.title),
- ),
- body: Center(
- child: Text(
- '您点击了$_count次',
- style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
- ),
- ),
- floatingActionButton: FloatingActionButton(
- child: const Icon(Icons.add),
- onPressed: () {
- setState(() {
- _count++;
- });
- },
- )
- );
- }
- }
Рисунок 1. Частичное обновление setState
Если страница относительно простая, вы можете напрямую использовать метод setState, чтобы частично обновить пользовательский интерфейс.
Сценарии использования: простые изменения состояния, такие как количество нажатий кнопок, состояние переключения и т. д.
Меры предосторожности:
StatefulWidget
является компонентом с состоянием,InheritedWidget
Используется для обмена данными в дереве компонентов.
Когда нам нужно поделиться данными, мы можем использовать StatefulWidget и InheritedWidget для частичного обновления пользовательского интерфейса.
Полный код выглядит следующим образом:
Рисунок 2. Обновление пользовательского интерфейса путем обмена данными
- import 'package:flutter/material.dart';
-
- class MyInheritedWidget extends InheritedWidget {
- final int counter;
- const MyInheritedWidget({
- super.key,
- required this.counter,
- required super.child,
- });
- @override
- bool updateShouldNotify(covariant InheritedWidget oldWidget) {
- return true;
- }
- static MyInheritedWidget? of(BuildContext context) {
- return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
- }
- }
-
- class InheritedWidgetPage extends StatefulWidget {
- final String title;
- const InheritedWidgetPage({super.key, required this.title});
-
- @override
- State<InheritedWidgetPage> createState() => _InheritedWidgetPageState();
- }
-
- class _InheritedWidgetPageState extends State<InheritedWidgetPage> {
- int counter = 0;
- void _incrementCounter() {
- setState(() {
- counter++;
- });
- }
-
- @override
- Widget build(BuildContext context) {
- return MyInheritedWidget(
- counter: counter,
- child: Scaffold(
- appBar: AppBar(
- title: Text(widget.title),
- ),
- body: Center(child: Column(
- children: [
- const Divider(),
- const CounterDisplay(),
- const SizedBox(height: 20),
- ElevatedButton(
- onPressed: _incrementCounter,
- child: const Text('add'),
- ),
- ],
- ),),
- ),
- );
- }
- }
-
- class CounterDisplay extends StatelessWidget {
- const CounterDisplay({super.key});
- @override
- Widget build(BuildContext context) {
- final inheritedWidget = MyInheritedWidget.of(context);
- return Text('点击次数: ${inheritedWidget?.counter}');
- }
- }
Основные сценарии использования этого метода следующие: при совместном использовании состояний в дереве компонентов, таких как тема, настройки языка и т. д.
Преимущество в том, что обмен данными удобен, а введение кода
Недостатком является то, что его сложно использовать и это может повлиять на производительность.
ValueNotifier
Это простой инструмент управления статусом,ValueListenableBuilder
для мониторингаValueNotifier
Изменение.
Использование также очень простое:
1. Создать экземпляр ValueNotifier
2. Объект Widget, который нужно отслеживать, обернут ValueListenableBuilder.
3. Как изменить данные, инициированные событием
По сравнению с предыдущими методами этот метод очень прост и удобен в использовании, а его производительность также очень высока.
Недостатки: Может обрабатывать только простые изменения состояния.
Полный код выглядит следующим образом:
- import 'package:flutter/material.dart';
-
- class ValueNotifierPage extends StatefulWidget {
- final String title;
- const ValueNotifierPage({super.key, required this.title});
-
- @override
- State<ValueNotifierPage> createState() => _ValueNotifierPageState();
- }
-
- class _ValueNotifierPageState extends State<ValueNotifierPage> {
-
- final ValueNotifier<int> _counter = ValueNotifier<int>(0);
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(widget.title),
- ),
- body: Center(
- child: ValueListenableBuilder<int>(
- valueListenable: _counter,
- builder: (context, value, child) {
- return Text(
- '您点击了$value次',
- style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
- );
- },
- )
- ),
- floatingActionButton: FloatingActionButton(
- child: const Icon(Icons.add),
- onPressed: () {
- _counter.value ++;
- },
- )
- );
- }
- }
Stream — это объект, используемый для доставки асинхронных событий, и события можно отправлять через StreamController. Если вам необходимо обновить пользовательский интерфейс, вы можете отправить событие в поток, а затем использовать StreamBuilder для прослушивания потока. При получении нового события StreamBuilder автоматически перестроит пользовательский интерфейс. Этот метод подходит для ситуаций, когда необходимо отслеживать несколько асинхронных событий.
Когда нам нужно обрабатывать асинхронные потоки данных, такие как сетевые запросы, данные в реальном времени и т. д., мы можем рассмотреть возможность использования StreamBuilder. Например, в следующем примере мы написали асинхронный метод, имитирующий сетевой запрос. Если сетевой запрос не возвращает правильный результат, мы можем загрузить индикатор выполнения.
Преимущество этого метода в том, что он позволяет более точно контролировать асинхронные запросы, например состояние сетевых запросов и т. д. Однако Dior более сложен и может потребовать больше кода.
Полный код выглядит следующим образом:
- import 'dart:async';
- import 'package:flutter/material.dart';
-
- class StreamBuilderRefreshUIPage extends StatefulWidget {
- final String title;
- const StreamBuilderRefreshUIPage({super.key, required this.title});
-
- @override
- State<StreamBuilderRefreshUIPage> createState() =>
- _StreamBuilderRefreshUIPageState();
- }
-
- class _StreamBuilderRefreshUIPageState extends State<StreamBuilderRefreshUIPage> {
- late Future<String> _data;
-
- Future<String> fetchData() async {
- // 模拟网络请求延迟
- await Future.delayed(const Duration(seconds: 2));
- // 返回模拟数据
- return 'Hello, Flutter!';
- }
-
- @override
- void initState() {
- // TODO: implement initState
- super.initState();
- _data = fetchData();
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(widget.title),
- ),
- body: Center(
- child: FutureBuilder<String>(
- future: _data,
- builder: (context, snapshot) {
- if (snapshot.connectionState == ConnectionState.waiting) {
- return const CircularProgressIndicator();
- } else if (snapshot.hasError) {
- return Text('Error: ${snapshot.error}');
- } else {
- return Text('Data: ${snapshot.data}');
- }
- },
- ),
- ),
- floatingActionButton: FloatingActionButton(
- onPressed: fetchData,
- tooltip: 'Increment',
- child: const Icon(Icons.add),
- ),
- );
- }
- }
Provider
Это решение для управления состоянием, рекомендованное Flutter.Consumer
Используется для чтения и мониторинга состояния.
Давайте также возьмем таймер в качестве примера.
1. Сначала импортируем Provider.
провайдер: ^6.1.2
2. Настройте класс ChangeNotifier.
ChangeNotifier — это простой класс в Flutter SDK. Он используется для отправки уведомлений слушателям. Другими словами, если он определен как ChangeNotifier, вы можете подписаться на изменения его состояния. (Это похоже на знакомый шаблон наблюдателя).
В коде, который мы хотим реализовать, есть две переменные _counter1 и _counter2. Код определяется следующим образом:
- class CounterModel extends ChangeNotifier {
- int _counter1 = 0;
- int _counter2 = 0;
- void addCounter1(){
- debugPrint('counter:$_counter1');
- _counter1 += 1;
- notifyListeners();
- }
- void addCounter2(){
- debugPrint('counter:$_counter2');
- _counter2 += 1;
- notifyListeners();
- }
- }
3. Используйте Customer, чтобы обернуть виджет, который мы хотим обновить.
- Consumer<CounterModel>(
- builder: (context, counterModel, child) {
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Text('计数器1个数: ${counterModel._counter1}'),
- ElevatedButton(onPressed: (){
- counterModel.addCounter1();
- }, child: const Icon(Icons.add),),
- ],
- );
- },
- ),
4. Полный код выглядит следующим образом:
- import 'package:flutter/material.dart';
- import 'package:provider/provider.dart';
-
- class ProviderRefreshPage extends StatefulWidget {
- final String title;
- const ProviderRefreshPage({super.key, required this.title});
-
- @override
- State<ProviderRefreshPage> createState() => _ProviderRefreshPageState();
- }
-
- class CounterModel extends ChangeNotifier {
- int _counter1 = 0;
- int _counter2 = 0;
- void addCounter1(){
- debugPrint('counter:$_counter1');
- _counter1 += 1;
- notifyListeners();
- }
- void addCounter2(){
- debugPrint('counter:$_counter2');
- _counter2 += 1;
- notifyListeners();
- }
- }
-
- class _ProviderRefreshPageState extends State<ProviderRefreshPage> {
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(widget.title),
- ),
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: <Widget>[
- const SizedBox(height: 10,), // 添加一些间距
- const Divider(),
- const Text('计数器实例',style: TextStyle(fontSize: 12,fontWeight: FontWeight.bold),),
- Consumer<CounterModel>(
- builder: (context, counterModel, child) {
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Text('计数器1个数: ${counterModel._counter1}'),
- ElevatedButton(onPressed: (){
- counterModel.addCounter1();
- }, child: const Icon(Icons.add),),
- ],
- );
- },
- ),
-
- Consumer<CounterModel>(
- builder: (context, counterModel, child) {
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Text('计数器1个数: ${counterModel._counter2}'),
- ElevatedButton(onPressed: (){
- counterModel.addCounter2();
- }, child: const Icon(Icons.add),),
- ],
- );
- },
- ),
- const Divider(height: 20,),
- ],
- ),
- ),
- );
- }
- }
Мы также можем использовать GetX для реализации частичного обновления пользовательского интерфейса.
Сначала установите GetX:
get: ^4.6.6
Затем мы инкапсулируем переменные в GetxController.
- class CounterManagerController extends GetxController {
- final counter1 = 0.obs;
- final counter2 = 0.obs;
- void incrementCount1() {
- counter1.value++;
- }
- void incrementCount2() {
- counter2.value++;
- }
- }
Затем используйте Obx, чтобы обернуть виджет, который должен отображать логику.
Obx(()=> Text('Номер счетчика 1: ${controller.counter2.value}'))
Полный код выглядит следующим образом:
- import 'package:flutter/material.dart';
- import 'package:get/get.dart';
-
-
- class CounterManagerController extends GetxController {
- final counter1 = 0.obs;
- final counter2 = 0.obs;
- void incrementCount1() {
- counter1.value++;
- }
- void incrementCount2() {
- counter2.value++;
- }
- }
-
- class GetXRefreshUIPage extends StatelessWidget {
- final String title;
- const GetXRefreshUIPage({super.key, required this.title});
-
- @override
- Widget build(BuildContext context) {
- final CounterManagerController controller = Get.put(CounterManagerController());
- return Scaffold(
- appBar: AppBar(
- title: Text(title),
- ),
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: <Widget>[
- const SizedBox(
- height: 10,
- ), // 添加一些间距
- const Divider(),
- const Text(
- '计数器实例',
- style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Obx(()=> Text('计数器1个数: ${controller.counter1.value}')),
- ElevatedButton(
- onPressed: () {
- controller.incrementCount1();
- },
- child: const Icon(Icons.add),
- ),
- ],
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Obx(()=> Text('计数器1个数: ${controller.counter2.value}')),
- ElevatedButton(
- onPressed: () {
- controller.incrementCount2();
- },
- child: const Icon(Icons.add),
- ),
- ],
- ),
- const Divider(
- height: 20,
- ),
- ],
- ),
- ),
- );
- }
- }
Конечно, есть несколько других способов реализовать частичное обновление в GetX. Вы можете посмотреть его документацию. Вот лишь одна идея реализации.
Все три вышеуказанных метода реализации реализованы через платформу. Если вы не хотите импортировать эту платформу, мы можем использовать GlobalKey для реализации функции частичного обновления пользовательского интерфейса.
Ключ, который уникален во всем приложении. GlobalKey уникально идентифицирует элементы. GlobalKey обеспечивает доступ к связанным элементам, например:BuildContext
.дляStatefulWidgets
, GlobalKey также предоставляетState
Доступ.
В нашей демонстрации таймера, если мы частично обновляем пользовательский интерфейс через GlobalKey, сначала мы извлекаем виджет, который нужно частично обновить, и инкапсулируем его в отдельный компонент.
Полный код выглядит следующим образом. Мы инкапсулируем виджет для частичного обновления и предоставляем интерфейс для обновления внутренних данных onPressed.
- class CounterText extends StatefulWidget {
- const CounterText(Key key) : super(key: key);
- @override
- State<StatefulWidget> createState() {
- return CounterTextState();
- }
- }
-
- class CounterTextState extends State<CounterText> {
- String _text="0";
-
- @override
- Widget build(BuildContext context) {
- return Center(
- child: Text(_text,style: const TextStyle(fontSize: 20),),
- );
- }
- void onPressed(int count) {
- setState(() {
- _text = count.toString();
- });
- }
- }
Затем создайте экземпляр GlobaKey в нашем основном интерфейсе:
- int _count = 0;
- int _count2 = 0;
- GlobalKey<CounterTextState> textKey = GlobalKey();
- GlobalKey<CounterTextState> textKey2 = GlobalKey();
В случае необходимости обновления пользовательского интерфейса вызовите интерфейс, предоставленный на предыдущем шаге, через GlobalKey и обновите его.
Полный код выглядит следующим образом:
- import 'package:flutter/material.dart';
-
- class GlobalKeyRefreshPage extends StatefulWidget {
- final String title;
- const GlobalKeyRefreshPage({super.key, required this.title});
-
- @override
- State<GlobalKeyRefreshPage> createState() => _GlobalKeyRefreshPageState();
- }
-
- class _GlobalKeyRefreshPageState extends State<GlobalKeyRefreshPage> {
- int _count = 0;
- int _count2 = 0;
- //包裹你定义的需要更新的weight
- GlobalKey<CounterTextState> textKey = GlobalKey();
- GlobalKey<CounterTextState> textKey2 = GlobalKey();
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(widget.title),
- ),
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: <Widget>[
- const SizedBox(
- height: 10,
- ), // 添加一些间距
- const Divider(),
- const Text(
- '计数器实例',
- style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- CounterText(textKey),
- ElevatedButton(
- onPressed: () {
- _count++;
- textKey.currentState?.onPressed(_count);
- },
- child: const Icon(Icons.add),
- ),
- ],
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- CounterText(textKey2),
- ElevatedButton(
- onPressed: () {
- _count2++;
- textKey2.currentState?.onPressed(_count2);
- },
- child: const Icon(Icons.add),
- ),
- ],
- ),
- const Divider(
- height: 20,
- ),
- ],
- ),
- ),
- );
- }
- }
-
-
- class CounterText extends StatefulWidget {
- const CounterText(Key key) : super(key: key);
- @override
- State<StatefulWidget> createState() {
- return CounterTextState();
- }
- }
-
- class CounterTextState extends State<CounterText> {
- String _text="0";
-
- @override
- Widget build(BuildContext context) {
- return Center(
- child: Text(_text,style: const TextStyle(fontSize: 20),),
- );
- }
- void onPressed(int count) {
- setState(() {
- _text = count.toString();
- });
- }
- }