Condivisione della tecnologia

Diversi modi in cui Flutter può implementare l'aggiornamento parziale

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Sommario

Prefazione

1. L'importanza del rinnovamento locale

1. Concetto

2. Importanza

2. Diversi modi per implementare l'aggiornamento parziale

1. Utilizzare il metodo setState per l'aggiornamento parziale

2. Utilizzare StatefulWidget e InheritedWidget per aggiornare parzialmente l'interfaccia utente

3.ValueNotifier e ValueListenableBuilder

4.Costruttore di flussi

5. Fornitore

6.OttieniX

7. Utilizzare GlobalKey


Prefazione

In Flutter, la gestione dello stato si riferisce a come gestire e aggiornare lo stato dei dati nell'applicazione e aggiornare l'interfaccia utente in base ai cambiamenti di stato. Una gestione efficace dello stato può aiutare gli sviluppatori a creare applicazioni più efficienti e gestibili.

setState è il metodo di gestione dello stato più basilare in Flutter. Quando lo stato cambia, al framework verrà inviata una notifica per ricostruire l'interfaccia utente. Naturalmente sappiamo che quando chiamiamo il metodo setState, la pagina verrà ridisegnata. Quando il layout della pagina è più complesso, a volte dobbiamo solo aggiornare un'interfaccia utente separata. In questo momento, se utilizziamo il metodo setState, avrà prestazioni migliori. Consuma per ridisegnare l'interfaccia utente della pagina corrente.

Quali metodi sono disponibili in Flutter per aggiornare parzialmente l'interfaccia utente? Questo blog elenca diversi modi in cui Flutter può implementare l'aggiornamento parziale?

1. L'importanza del rinnovamento locale

1. Concetto

L'aggiornamento parziale si riferisce all'aggiornamento solo di una parte dell'interfaccia, non dell'intera pagina. Ciò migliora le prestazioni e l'esperienza dell'utente.

2. Importanza

  1. Evita ridisegnazioni non necessarie e migliora le prestazioni
  2. Fornire un'esperienza utente più fluida
  3. Ridurre il consumo di risorse

2. Diversi modi per implementare l'aggiornamento parziale

1. Utilizzare il metodo setState per l'aggiornamento parziale

setState è il metodo di gestione dello stato più comunemente utilizzato in Flutter. Viene utilizzato per notificare al framework le modifiche dello stato, portando alla ricostruzione dell'interfaccia.

Quando creiamo un progetto Flutter, l'esempio del timer generato dal sistema per impostazione predefinita è l'esempio dell'aggiornamento parziale setState.

  1. import 'package:flutter/material.dart';
  2. class SetStateMainPage extends StatefulWidget {
  3. final String title;
  4. const SetStateMainPage({super.key, required this.title});
  5. @override
  6. State<SetStateMainPage> createState() => _SetStateMainPageMainPageState();
  7. }
  8. class _SetStateMainPageMainPageState extends State<SetStateMainPage> {
  9. int _count = 0;
  10. @override
  11. Widget build(BuildContext context) {
  12. return Scaffold(
  13. appBar: AppBar(
  14. title: Text(widget.title),
  15. ),
  16. body: Center(
  17. child: Text(
  18. '您点击了$_count次',
  19. style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
  20. ),
  21. ),
  22. floatingActionButton: FloatingActionButton(
  23. child: const Icon(Icons.add),
  24. onPressed: () {
  25. setState(() {
  26. _count++;
  27. });
  28. },
  29. )
  30. );
  31. }
  32. }

Figura 1.setState aggiornamento parziale

Quando la pagina è relativamente semplice, puoi utilizzare direttamente il metodo setState per aggiornare parzialmente l'interfaccia utente.

Scenari di utilizzo: semplici modifiche di stato, come conteggio dei clic sui pulsanti, cambio di stato, ecc.

Precauzioni:

  1. Le chiamate frequenti a setState possono causare problemi di prestazioni
  2. Evitare di chiamare setState nel metodo build

2. Utilizzare StatefulWidget e InheritedWidget per aggiornare parzialmente l'interfaccia utente

        StatefulWidget è un componente con stato,InheritedWidget Utilizzato per condividere i dati all'interno dell'albero dei componenti.

Quando dobbiamo condividere dati, possiamo considerare StatefulWidget e InheritedWidget per aggiornare parzialmente l'interfaccia utente.

Il codice completo è il seguente:

Figura 2. Aggiorna l'interfaccia utente condividendo i dati

  1. import 'package:flutter/material.dart';
  2. class MyInheritedWidget extends InheritedWidget {
  3. final int counter;
  4. const MyInheritedWidget({
  5. super.key,
  6. required this.counter,
  7. required super.child,
  8. });
  9. @override
  10. bool updateShouldNotify(covariant InheritedWidget oldWidget) {
  11. return true;
  12. }
  13. static MyInheritedWidget? of(BuildContext context) {
  14. return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  15. }
  16. }
  17. class InheritedWidgetPage extends StatefulWidget {
  18. final String title;
  19. const InheritedWidgetPage({super.key, required this.title});
  20. @override
  21. State<InheritedWidgetPage> createState() => _InheritedWidgetPageState();
  22. }
  23. class _InheritedWidgetPageState extends State<InheritedWidgetPage> {
  24. int counter = 0;
  25. void _incrementCounter() {
  26. setState(() {
  27. counter++;
  28. });
  29. }
  30. @override
  31. Widget build(BuildContext context) {
  32. return MyInheritedWidget(
  33. counter: counter,
  34. child: Scaffold(
  35. appBar: AppBar(
  36. title: Text(widget.title),
  37. ),
  38. body: Center(child: Column(
  39. children: [
  40. const Divider(),
  41. const CounterDisplay(),
  42. const SizedBox(height: 20),
  43. ElevatedButton(
  44. onPressed: _incrementCounter,
  45. child: const Text('add'),
  46. ),
  47. ],
  48. ),),
  49. ),
  50. );
  51. }
  52. }
  53. class CounterDisplay extends StatelessWidget {
  54. const CounterDisplay({super.key});
  55. @override
  56. Widget build(BuildContext context) {
  57. final inheritedWidget = MyInheritedWidget.of(context);
  58. return Text('点击次数: ${inheritedWidget?.counter}');
  59. }
  60. }

I principali scenari di utilizzo di questo metodo sono i seguenti: quando si condivide lo stato nell'albero dei componenti, come tema, impostazioni della lingua, ecc.

Il vantaggio è che la condivisione dei dati è conveniente e l'introduzione del codice

Lo svantaggio è che è complicato da usare e le prestazioni potrebbero risentirne.

3.ValueNotifier e ValueListenableBuilder

        ValueNotifier È un semplice strumento di gestione dello stato,ValueListenableBuilder per il monitoraggioValueNotifier Il cambiamento.

Anche l'utilizzo è molto semplice:

1. Istanziare ValueNotifier

2. L'oggetto Widget da monitorare viene racchiuso con ValueListenableBuilder

3. Come modificare i dati attivati ​​da eventi

Rispetto ai metodi precedenti, questo metodo è molto semplice e facile da usare e anche le sue prestazioni sono molto elevate.

Svantaggi: può gestire solo semplici cambiamenti di stato

Il codice completo è il seguente:

  1. import 'package:flutter/material.dart';
  2. class ValueNotifierPage extends StatefulWidget {
  3. final String title;
  4. const ValueNotifierPage({super.key, required this.title});
  5. @override
  6. State<ValueNotifierPage> createState() => _ValueNotifierPageState();
  7. }
  8. class _ValueNotifierPageState extends State<ValueNotifierPage> {
  9. final ValueNotifier<int> _counter = ValueNotifier<int>(0);
  10. @override
  11. Widget build(BuildContext context) {
  12. return Scaffold(
  13. appBar: AppBar(
  14. title: Text(widget.title),
  15. ),
  16. body: Center(
  17. child: ValueListenableBuilder<int>(
  18. valueListenable: _counter,
  19. builder: (context, value, child) {
  20. return Text(
  21. '您点击了$value次',
  22. style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
  23. );
  24. },
  25. )
  26. ),
  27. floatingActionButton: FloatingActionButton(
  28. child: const Icon(Icons.add),
  29. onPressed: () {
  30. _counter.value ++;
  31. },
  32. )
  33. );
  34. }
  35. }

4.Costruttore di flussi

Stream è un oggetto utilizzato per fornire eventi asincroni e gli eventi possono essere inviati tramite StreamController. Se è necessario aggiornare l'interfaccia utente, è possibile inviare un evento allo stream e quindi utilizzare StreamBuilder per ascoltare lo stream. Quando viene ricevuto un nuovo evento, StreamBuilder ricostruirà automaticamente l'interfaccia utente. Questo metodo è adatto per situazioni in cui è necessario monitorare più eventi asincroni.

Quando dobbiamo elaborare flussi di dati asincroni, come richieste di rete, dati in tempo reale, ecc., possiamo prendere in considerazione l'utilizzo di StreamBuilder. Ad esempio, nell'esempio seguente, abbiamo scritto un metodo asincrono che simula una richiesta di rete. Quando la richiesta di rete non restituisce il risultato corretto, possiamo caricare la barra di avanzamento.

Il vantaggio di questo metodo è che può controllare in modo più accurato le richieste asincrone, come lo stato delle richieste di rete, ecc. Tuttavia, Dior è più complesso e potrebbe richiedere più codice.

Il codice completo è il seguente:

  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. class StreamBuilderRefreshUIPage extends StatefulWidget {
  4. final String title;
  5. const StreamBuilderRefreshUIPage({super.key, required this.title});
  6. @override
  7. State<StreamBuilderRefreshUIPage> createState() =>
  8. _StreamBuilderRefreshUIPageState();
  9. }
  10. class _StreamBuilderRefreshUIPageState extends State<StreamBuilderRefreshUIPage> {
  11. late Future<String> _data;
  12. Future<String> fetchData() async {
  13. // 模拟网络请求延迟
  14. await Future.delayed(const Duration(seconds: 2));
  15. // 返回模拟数据
  16. return 'Hello, Flutter!';
  17. }
  18. @override
  19. void initState() {
  20. // TODO: implement initState
  21. super.initState();
  22. _data = fetchData();
  23. }
  24. @override
  25. Widget build(BuildContext context) {
  26. return Scaffold(
  27. appBar: AppBar(
  28. title: Text(widget.title),
  29. ),
  30. body: Center(
  31. child: FutureBuilder<String>(
  32. future: _data,
  33. builder: (context, snapshot) {
  34. if (snapshot.connectionState == ConnectionState.waiting) {
  35. return const CircularProgressIndicator();
  36. } else if (snapshot.hasError) {
  37. return Text('Error: ${snapshot.error}');
  38. } else {
  39. return Text('Data: ${snapshot.data}');
  40. }
  41. },
  42. ),
  43. ),
  44. floatingActionButton: FloatingActionButton(
  45. onPressed: fetchData,
  46. tooltip: 'Increment',
  47. child: const Icon(Icons.add),
  48. ),
  49. );
  50. }
  51. }

5. Fornitore

       Provider È una soluzione di gestione dello stato consigliata da Flutter.Consumer Utilizzato per leggere e monitorare lo stato.

Prendiamo come esempio anche il timer.

1. Per prima cosa importiamo Provider.

fornitore: ^6.1.2

2. Personalizza la classe ChangeNotifier.

ChangeNotifier è una classe semplice nell'SDK di Flutter. Viene utilizzato per inviare notifiche agli ascoltatori. In altre parole, se definito come ChangeNotifier, puoi iscriverti ai suoi cambiamenti di stato. (Questo è simile al modello familiare dell'osservatore).

Nel codice che vogliamo implementare sono presenti due variabili _counter1 e _counter2. Il codice è definito come segue:

  1. class CounterModel extends ChangeNotifier {
  2. int _counter1 = 0;
  3. int _counter2 = 0;
  4. void addCounter1(){
  5. debugPrint('counter:$_counter1');
  6. _counter1 += 1;
  7. notifyListeners();
  8. }
  9. void addCounter2(){
  10. debugPrint('counter:$_counter2');
  11. _counter2 += 1;
  12. notifyListeners();
  13. }
  14. }

3. Utilizza Customer per racchiudere il widget che vogliamo aggiornare

  1. Consumer<CounterModel>(
  2. builder: (context, counterModel, child) {
  3. return Row(
  4. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  5. children: [
  6. Text('计数器1个数: ${counterModel._counter1}'),
  7. ElevatedButton(onPressed: (){
  8. counterModel.addCounter1();
  9. }, child: const Icon(Icons.add),),
  10. ],
  11. );
  12. },
  13. ),

4. Il codice completo è il seguente:

  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. class ProviderRefreshPage extends StatefulWidget {
  4. final String title;
  5. const ProviderRefreshPage({super.key, required this.title});
  6. @override
  7. State<ProviderRefreshPage> createState() => _ProviderRefreshPageState();
  8. }
  9. class CounterModel extends ChangeNotifier {
  10. int _counter1 = 0;
  11. int _counter2 = 0;
  12. void addCounter1(){
  13. debugPrint('counter:$_counter1');
  14. _counter1 += 1;
  15. notifyListeners();
  16. }
  17. void addCounter2(){
  18. debugPrint('counter:$_counter2');
  19. _counter2 += 1;
  20. notifyListeners();
  21. }
  22. }
  23. class _ProviderRefreshPageState extends State<ProviderRefreshPage> {
  24. @override
  25. Widget build(BuildContext context) {
  26. return Scaffold(
  27. appBar: AppBar(
  28. title: Text(widget.title),
  29. ),
  30. body: Center(
  31. child: Column(
  32. mainAxisAlignment: MainAxisAlignment.start,
  33. children: <Widget>[
  34. const SizedBox(height: 10,), // 添加一些间距
  35. const Divider(),
  36. const Text('计数器实例',style: TextStyle(fontSize: 12,fontWeight: FontWeight.bold),),
  37. Consumer<CounterModel>(
  38. builder: (context, counterModel, child) {
  39. return Row(
  40. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  41. children: [
  42. Text('计数器1个数: ${counterModel._counter1}'),
  43. ElevatedButton(onPressed: (){
  44. counterModel.addCounter1();
  45. }, child: const Icon(Icons.add),),
  46. ],
  47. );
  48. },
  49. ),
  50. Consumer<CounterModel>(
  51. builder: (context, counterModel, child) {
  52. return Row(
  53. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  54. children: [
  55. Text('计数器1个数: ${counterModel._counter2}'),
  56. ElevatedButton(onPressed: (){
  57. counterModel.addCounter2();
  58. }, child: const Icon(Icons.add),),
  59. ],
  60. );
  61. },
  62. ),
  63. const Divider(height: 20,),
  64. ],
  65. ),
  66. ),
  67. );
  68. }
  69. }

6.OttieniX

Possiamo anche utilizzare GetX per implementare un aggiornamento parziale dell'interfaccia utente.

Per prima cosa installa GetX:

get: ^4.6.6

Quindi incapsuliamo le variabili in GetxController.

  1. class CounterManagerController extends GetxController {
  2. final counter1 = 0.obs;
  3. final counter2 = 0.obs;
  4. void incrementCount1() {
  5. counter1.value++;
  6. }
  7. void incrementCount2() {
  8. counter2.value++;
  9. }
  10. }

Quindi usa Obx per avvolgere il widget che deve visualizzare la logica.

Obx(()=&gt; Text('Numero contatore 1: ${controller.counter2.value}'))

Il codice completo è il seguente:

  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. class CounterManagerController extends GetxController {
  4. final counter1 = 0.obs;
  5. final counter2 = 0.obs;
  6. void incrementCount1() {
  7. counter1.value++;
  8. }
  9. void incrementCount2() {
  10. counter2.value++;
  11. }
  12. }
  13. class GetXRefreshUIPage extends StatelessWidget {
  14. final String title;
  15. const GetXRefreshUIPage({super.key, required this.title});
  16. @override
  17. Widget build(BuildContext context) {
  18. final CounterManagerController controller = Get.put(CounterManagerController());
  19. return Scaffold(
  20. appBar: AppBar(
  21. title: Text(title),
  22. ),
  23. body: Center(
  24. child: Column(
  25. mainAxisAlignment: MainAxisAlignment.start,
  26. children: <Widget>[
  27. const SizedBox(
  28. height: 10,
  29. ), // 添加一些间距
  30. const Divider(),
  31. const Text(
  32. '计数器实例',
  33. style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
  34. ),
  35. Row(
  36. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  37. children: [
  38. Obx(()=> Text('计数器1个数: ${controller.counter1.value}')),
  39. ElevatedButton(
  40. onPressed: () {
  41. controller.incrementCount1();
  42. },
  43. child: const Icon(Icons.add),
  44. ),
  45. ],
  46. ),
  47. Row(
  48. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  49. children: [
  50. Obx(()=> Text('计数器1个数: ${controller.counter2.value}')),
  51. ElevatedButton(
  52. onPressed: () {
  53. controller.incrementCount2();
  54. },
  55. child: const Icon(Icons.add),
  56. ),
  57. ],
  58. ),
  59. const Divider(
  60. height: 20,
  61. ),
  62. ],
  63. ),
  64. ),
  65. );
  66. }
  67. }

Naturalmente, ci sono molti altri modi per implementare l'aggiornamento parziale in GetX. Puoi dare un'occhiata alla sua documentazione. Ecco solo un'idea di implementazione.

7. Utilizzare GlobalKey

I tre metodi di implementazione precedenti sono tutti implementati tramite il framework Se non si desidera importare questo framework, è possibile utilizzare GlobalKey per implementare la funzione di aggiornamento parziale dell'interfaccia utente.

Una chiave univoca in tutta l'applicazione GlobalKey identifica in modo univoco gli elementi GlobalKey fornisce l'accesso agli elementi associati, ad esempioBuildContext .perStatefulWidgets, GlobalKey fornisce ancheStateAccesso.

Nella nostra demo del timer, se aggiorniamo parzialmente l'interfaccia utente tramite GlobalKey, prima estraiamo il widget da aggiornare parzialmente e lo incapsuliamo in un componente separato.

Il codice completo è il seguente. Incapsuliamo il Widget da aggiornare parzialmente e forniamo un'interfaccia per l'aggiornamento dei dati interni, onPressed.

  1. class CounterText extends StatefulWidget {
  2. const CounterText(Key key) : super(key: key);
  3. @override
  4. State<StatefulWidget> createState() {
  5. return CounterTextState();
  6. }
  7. }
  8. class CounterTextState extends State<CounterText> {
  9. String _text="0";
  10. @override
  11. Widget build(BuildContext context) {
  12. return Center(
  13. child: Text(_text,style: const TextStyle(fontSize: 20),),
  14. );
  15. }
  16. void onPressed(int count) {
  17. setState(() {
  18. _text = count.toString();
  19. });
  20. }
  21. }

Quindi istanzia GlobaKey nella nostra interfaccia principale:

  1. int _count = 0;
  2. int _count2 = 0;
  3. GlobalKey<CounterTextState> textKey = GlobalKey();
  4. GlobalKey<CounterTextState> textKey2 = GlobalKey();

Nel caso in cui sia necessario aggiornare l'interfaccia utente, richiamare l'interfaccia fornita nel passaggio precedente tramite GlobalKey e aggiornare.

Il codice completo è il seguente:

  1. import 'package:flutter/material.dart';
  2. class GlobalKeyRefreshPage extends StatefulWidget {
  3. final String title;
  4. const GlobalKeyRefreshPage({super.key, required this.title});
  5. @override
  6. State<GlobalKeyRefreshPage> createState() => _GlobalKeyRefreshPageState();
  7. }
  8. class _GlobalKeyRefreshPageState extends State<GlobalKeyRefreshPage> {
  9. int _count = 0;
  10. int _count2 = 0;
  11. //包裹你定义的需要更新的weight
  12. GlobalKey<CounterTextState> textKey = GlobalKey();
  13. GlobalKey<CounterTextState> textKey2 = GlobalKey();
  14. @override
  15. Widget build(BuildContext context) {
  16. return Scaffold(
  17. appBar: AppBar(
  18. title: Text(widget.title),
  19. ),
  20. body: Center(
  21. child: Column(
  22. mainAxisAlignment: MainAxisAlignment.start,
  23. children: <Widget>[
  24. const SizedBox(
  25. height: 10,
  26. ), // 添加一些间距
  27. const Divider(),
  28. const Text(
  29. '计数器实例',
  30. style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
  31. ),
  32. Row(
  33. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  34. children: [
  35. CounterText(textKey),
  36. ElevatedButton(
  37. onPressed: () {
  38. _count++;
  39. textKey.currentState?.onPressed(_count);
  40. },
  41. child: const Icon(Icons.add),
  42. ),
  43. ],
  44. ),
  45. Row(
  46. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  47. children: [
  48. CounterText(textKey2),
  49. ElevatedButton(
  50. onPressed: () {
  51. _count2++;
  52. textKey2.currentState?.onPressed(_count2);
  53. },
  54. child: const Icon(Icons.add),
  55. ),
  56. ],
  57. ),
  58. const Divider(
  59. height: 20,
  60. ),
  61. ],
  62. ),
  63. ),
  64. );
  65. }
  66. }
  67. class CounterText extends StatefulWidget {
  68. const CounterText(Key key) : super(key: key);
  69. @override
  70. State<StatefulWidget> createState() {
  71. return CounterTextState();
  72. }
  73. }
  74. class CounterTextState extends State<CounterText> {
  75. String _text="0";
  76. @override
  77. Widget build(BuildContext context) {
  78. return Center(
  79. child: Text(_text,style: const TextStyle(fontSize: 20),),
  80. );
  81. }
  82. void onPressed(int count) {
  83. setState(() {
  84. _text = count.toString();
  85. });
  86. }
  87. }