2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Inhaltsverzeichnis
1. Die Bedeutung der lokalen Aktualisierung
2. Mehrere Möglichkeiten zur Implementierung einer teilweisen Aktualisierung
1. Verwenden Sie die setState-Methode für eine teilweise Aktualisierung
3.ValueNotifier und ValueListenableBuilder
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.
Unter teilweiser Aktualisierung versteht man die Aktualisierung nur eines Teils der Benutzeroberfläche, nicht der gesamten Seite. Dies verbessert die Leistung und das Benutzererlebnis.
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.
- 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++;
- });
- },
- )
- );
- }
- }
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:
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
- 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}');
- }
- }
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.
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:
- 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 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:
- 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
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:
- 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. Verwenden Sie „Customer“, um das Widget zu umschließen, das wir aktualisieren möchten
- 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. Der vollständige Code lautet wie folgt:
- 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,),
- ],
- ),
- ),
- );
- }
- }
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.
- class CounterManagerController extends GetxController {
- final counter1 = 0.obs;
- final counter2 = 0.obs;
- void incrementCount1() {
- counter1.value++;
- }
- void incrementCount2() {
- counter2.value++;
- }
- }
Verwenden Sie dann Obx, um das Widget zu umschließen, das Logik anzeigen muss.
Obx(()=> Text('Zähler 1 Nummer: ${controller.counter2.value}'))
Der vollständige Code lautet wie folgt:
- 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,
- ),
- ],
- ),
- ),
- );
- }
- }
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.
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 auchState
Zugang.
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.
- 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();
- });
- }
- }
Dann instanziieren Sie GlobaKey in unserer Hauptschnittstelle:
- int _count = 0;
- int _count2 = 0;
- GlobalKey<CounterTextState> textKey = GlobalKey();
- 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:
- 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();
- });
- }
- }