Technologieaustausch

Mehrere Möglichkeiten für Flutter, eine teilweise Aktualisierung zu implementieren

2024-07-12

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

Inhaltsverzeichnis

Vorwort

1. Die Bedeutung der lokalen Aktualisierung

1. Konzept

2. Bedeutung

2. Mehrere Möglichkeiten zur Implementierung einer teilweisen Aktualisierung

1. Verwenden Sie die setState-Methode für eine teilweise Aktualisierung

2. Verwenden Sie StatefulWidget und InheritedWidget, um die Benutzeroberfläche teilweise zu aktualisieren

3.ValueNotifier und ValueListenableBuilder

4.StreamBuilder

5.Anbieter

6.GetX

7. Verwenden Sie GlobalKey


Vorwort

In Flutter bezieht sich die Statusverwaltung auf die Verwaltung und Aktualisierung des Datenstatus in der Anwendung sowie auf die Aktualisierung der Benutzeroberfläche entsprechend Statusänderungen. Eine effektive Zustandsverwaltung kann Entwicklern dabei helfen, effizientere und wartbarere Anwendungen zu erstellen.

setState ist die grundlegendste Statusverwaltungsmethode in Flutter. Wenn sich der Status ändert, wird das Framework benachrichtigt, um die Benutzeroberfläche neu zu erstellen. Natürlich wissen wir, dass die Seite beim Aufrufen der setState-Methode neu gezeichnet wird. Wenn das Seitenlayout komplexer ist, müssen wir zu diesem Zeitpunkt nur eine separate Benutzeroberfläche aktualisieren höhere Leistung. Verbrauchen, um die Benutzeroberfläche der aktuellen Seite neu zu zeichnen.

Welche Methoden gibt es in Flutter, um die Benutzeroberfläche teilweise zu aktualisieren? In diesem Blog werden verschiedene Möglichkeiten für Flutter aufgeführt, eine teilweise Aktualisierung zu implementieren.

1. Die Bedeutung der lokalen Aktualisierung

1. Konzept

Unter teilweiser Aktualisierung versteht man die Aktualisierung nur eines Teils der Benutzeroberfläche, nicht der gesamten Seite. Dies verbessert die Leistung und das Benutzererlebnis.

2. Bedeutung

  1. Vermeiden Sie unnötige Neuzeichnungen und verbessern Sie die Leistung
  2. Sorgen Sie für ein reibungsloseres Benutzererlebnis
  3. Reduzieren Sie den Ressourcenverbrauch

2. Mehrere Möglichkeiten zur Implementierung einer teilweisen Aktualisierung

1. Verwenden Sie die setState-Methode für eine teilweise Aktualisierung

setState ist die am häufigsten verwendete Zustandsverwaltungsmethode in Flutter. Sie wird verwendet, um das Framework über Zustandsänderungen zu informieren, die zu einer Schnittstellenrekonstruktion führen.

Wenn wir ein Flutter-Projekt erstellen, ist das Beispiel des vom System standardmäßig generierten Timers das Beispiel der teilweisen Aktualisierung von 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. }

Abbildung 1.setState-Teilaktualisierung

Wenn die Seite relativ einfach ist, können Sie die setState-Methode direkt verwenden, um die Benutzeroberfläche teilweise zu aktualisieren.

Verwendungsszenarien: einfache Zustandsänderungen, z. B. Anzahl der Tastenklicks, Wechselzustand usw.

Vorsichtsmaßnahmen:

  1. Häufige Aufrufe von setState können zu Leistungsproblemen führen
  2. Vermeiden Sie den Aufruf von setState in der Build-Methode

2. Verwenden Sie StatefulWidget und InheritedWidget, um die Benutzeroberfläche teilweise zu aktualisieren

        StatefulWidget ist eine Komponente mit Zustand,InheritedWidget Wird zum Teilen von Daten innerhalb des Komponentenbaums verwendet.

Wenn wir Daten teilen müssen, können wir StatefulWidget und InheritedWidget in Betracht ziehen, um die Benutzeroberfläche teilweise zu aktualisieren.

Der vollständige Code lautet wie folgt:

Abbildung 2. Aktualisieren Sie die Benutzeroberfläche, indem Sie Daten teilen

  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. }

Die Hauptverwendungsszenarien dieser Methode sind wie folgt: beim Teilen des Status im Komponentenbaum, z. B. Thema, Spracheinstellungen usw.

Der Vorteil besteht darin, dass der Datenaustausch und die Codeeinführung bequem sind

Der Nachteil besteht darin, dass die Verwendung kompliziert ist und die Leistung beeinträchtigt sein kann.

3.ValueNotifier und ValueListenableBuilder

        ValueNotifier Es handelt sich um ein einfaches Statusverwaltungstool.ValueListenableBuilder Zur ÜberwachungValueNotifier Der Wechsel.

Auch die Verwendung ist sehr einfach:

1. ValueNotifier instanziieren

2. Das zu überwachende Widget-Objekt wird mit ValueListenableBuilder umschlossen

3. So ändern Sie ereignisgesteuerte Daten

Im Vergleich zu den vorherigen Methoden ist diese Methode sehr einfach und benutzerfreundlich und weist auch eine sehr hohe Leistung auf.

Nachteile: Kann nur einfache Zustandsänderungen verarbeiten

Der vollständige Code lautet wie folgt:

  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.StreamBuilder

Stream ist ein Objekt, das zur Bereitstellung asynchroner Ereignisse verwendet wird. Ereignisse können über StreamController gesendet werden. Wenn Sie die Benutzeroberfläche aktualisieren müssen, können Sie ein Ereignis an den Stream senden und dann StreamBuilder verwenden, um den Stream abzuhören. Wenn ein neues Ereignis empfangen wird, erstellt StreamBuilder die Benutzeroberfläche automatisch neu. Diese Methode eignet sich für Situationen, in denen mehrere asynchrone Ereignisse überwacht werden müssen.

Wenn wir asynchrone Datenströme wie Netzwerkanforderungen, Echtzeitdaten usw. verarbeiten müssen, können wir die Verwendung von StreamBuilder in Betracht ziehen. Im folgenden Beispiel haben wir beispielsweise eine asynchrone Methode geschrieben, die eine Netzwerkanfrage simuliert. Wenn die Netzwerkanfrage nicht das richtige Ergebnis zurückgibt, können wir den Fortschrittsbalken laden.

Der Vorteil dieser Methode besteht darin, dass asynchrone Anforderungen genauer gesteuert werden können, z. B. der Status von Netzwerkanforderungen usw. Allerdings ist Dior komplexer und erfordert möglicherweise mehr Code.

Der vollständige Code lautet wie folgt:

  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.Anbieter

       Provider Es handelt sich um eine von Flutter empfohlene Zustandsverwaltungslösung.Consumer Wird zum Lesen und Überwachen des Status verwendet.

Nehmen wir auch den Timer als Beispiel.

1. Zuerst importieren wir Provider.

Anbieter: ^6.1.2

2. Passen Sie die ChangeNotifier-Klasse an.

ChangeNotifier ist eine einfache Klasse im Flutter SDK. Es wird verwendet, um Benachrichtigungen an Zuhörer zu senden. Mit anderen Worten: Wenn es als ChangeNotifier definiert ist, können Sie dessen Statusänderungen abonnieren. (Dies ähnelt dem bekannten Beobachtermuster).

In dem Code, den wir implementieren möchten, gibt es zwei Variablen _counter1 und _counter2. Der Code ist wie folgt definiert:

  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. Verwenden Sie „Customer“, um das Widget zu umschließen, das wir aktualisieren möchten

  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. Der vollständige Code lautet wie folgt:

  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.GetX

Wir können GetX auch verwenden, um eine teilweise Aktualisierung der Benutzeroberfläche zu implementieren.

Installieren Sie zuerst GetX:

get: ^4.6.6

Dann kapseln wir die Variablen 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. }

Verwenden Sie dann Obx, um das Widget zu umschließen, das Logik anzeigen muss.

Obx(()=&gt; Text('Zähler 1 Nummer: ${controller.counter2.value}'))

Der vollständige Code lautet wie folgt:

  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. }

Natürlich gibt es mehrere andere Möglichkeiten, eine teilweise Aktualisierung in GetX zu implementieren. Sie können einen Blick auf die Dokumentation werfen. Hier ist nur eine Umsetzungsidee.

7. Verwenden Sie GlobalKey

Die oben genannten drei Implementierungsmethoden werden alle über das Framework implementiert. Wenn Sie dieses Framework nicht importieren möchten, können wir GlobalKey verwenden, um die teilweise Aktualisierungsfunktion der Benutzeroberfläche zu implementieren.

Ein in der gesamten Anwendung eindeutiger GlobalKey identifiziert Elemente eindeutig. GlobalKey ermöglicht den Zugriff auf die zugehörigen Elemente, zBuildContext .fürStatefulWidgets, GlobalKey bietet auchStateZugang.

Wenn wir in unserer Timer-Demo die Benutzeroberfläche teilweise über GlobalKey aktualisieren, nehmen wir zuerst das Widget heraus, das teilweise aktualisiert werden soll, und kapseln es in eine separate Komponente.

Der vollständige Code lautet wie folgt. Wir kapseln das teilweise zu aktualisierende Widget und stellen eine Schnittstelle zum Aktualisieren interner Daten bereit: 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. }

Dann instanziieren Sie GlobaKey in unserer Hauptschnittstelle:

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

Falls die Benutzeroberfläche aktualisiert werden muss, rufen Sie die im vorherigen Schritt bereitgestellte Schnittstelle über GlobalKey auf und aktualisieren Sie sie.

Der vollständige Code lautet wie folgt:

  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. }