2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Table des matières
1. L’importance du rafraîchissement local
2. Plusieurs façons de mettre en œuvre un rafraîchissement partiel
1. Utilisez la méthode setState pour une actualisation partielle
2. Utilisez StatefulWidget et InheritedWidget pour actualiser partiellement l'interface utilisateur
3.ValueNotifier et ValueListenableBuilder
Dans Flutter, la gestion de l'état fait référence à la manière de gérer et de mettre à jour l'état des données dans l'application, ainsi que de mettre à jour l'interface utilisateur en fonction des changements d'état. Une gestion efficace de l'état peut aider les développeurs à créer des applications plus efficaces et plus maintenables.
setState est la méthode de gestion d'état la plus basique dans Flutter. Lorsque l'état change, le framework sera informé de la reconstruction de l'interface utilisateur. Bien sûr, nous savons que lorsque nous appelons la méthode setState, la page sera redessinée. Lorsque la mise en page est plus complexe, il suffit parfois de mettre à jour une interface utilisateur distincte. À ce stade, si nous utilisons la méthode setState, elle aura. de meilleures performances. Consommer pour redessiner l'interface utilisateur de la page actuelle.
Alors, quelles méthodes existe-t-il dans Flutter pour actualiser partiellement l'interface utilisateur ? Ce blog répertorie plusieurs façons permettant à Flutter d'implémenter une actualisation partielle.
L'actualisation partielle fait référence à l'actualisation d'une seule partie de l'interface, et non de la page entière. Cela améliore les performances et l’expérience utilisateur.
setState est la méthode de gestion d'état la plus couramment utilisée dans Flutter. Elle est utilisée pour notifier le framework des changements d'état, conduisant à la reconstruction de l'interface.
Lorsque nous créons un projet Flutter, l'exemple du timer généré par le système par défaut est l'exemple du rafraîchissement partiel 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++;
- });
- },
- )
- );
- }
- }
Figure 1. Actualisation partielle setState
Lorsque la page est relativement simple, vous pouvez directement utiliser la méthode setState pour actualiser partiellement l'interface utilisateur.
Scénarios d'utilisation : changements d'état simples, tels que le nombre de clics sur les boutons, l'état du commutateur, etc.
Précautions:
StatefulWidget
est un composant avec état,InheritedWidget
Utilisé pour partager des données dans l'arborescence des composants.
Lorsque nous avons besoin de partager des données, nous pouvons envisager StatefulWidget et InheritedWidget pour actualiser partiellement l'interface utilisateur.
Le code complet est le suivant :
Figure 2. Actualiser l'interface utilisateur en partageant des données
- 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}');
- }
- }
Les principaux scénarios d'utilisation de cette méthode sont les suivants : lors du partage de l'état dans l'arborescence des composants, comme le thème, les paramètres de langue, etc.
L'avantage est que le partage des données est pratique et que l'introduction du code
L’inconvénient est qu’il est compliqué à utiliser et que les performances peuvent être affectées.
ValueNotifier
C'est un outil simple de gestion de statut,ValueListenableBuilder
pour la surveillanceValueNotifier
Le changement.
L'utilisation est également très simple :
1. Instancier ValueNotifier
2. L'objet Widget à surveiller est encapsulé avec ValueListenableBuilder
3. Comment modifier les données déclenchées par un événement
Par rapport aux méthodes précédentes, cette méthode est très simple et facile à utiliser, et ses performances sont également très élevées.
Inconvénients : ne peut gérer que de simples changements d’état
Le code complet est le suivant :
- 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 est un objet utilisé pour diffuser des événements asynchrones, et les événements peuvent être envoyés via StreamController. Lorsque vous devez actualiser l'interface utilisateur, vous pouvez envoyer un événement au Stream, puis utiliser StreamBuilder pour écouter le Stream. Lorsqu'un nouvel événement est reçu, StreamBuilder reconstruira automatiquement l'interface utilisateur. Cette méthode convient aux situations dans lesquelles plusieurs événements asynchrones doivent être surveillés.
Lorsque nous devons traiter des flux de données asynchrones, tels que des requêtes réseau, des données en temps réel, etc., nous pouvons envisager d'utiliser StreamBuilder. Par exemple, dans l'exemple suivant, nous avons écrit une méthode asynchrone qui simule une requête réseau. Lorsque la requête réseau ne renvoie pas le résultat correct, nous pouvons charger la barre de progression.
L'avantage de cette méthode est qu'elle permet de contrôler plus précisément les requêtes asynchrones, comme l'état des requêtes réseau, etc. Cependant, Dior est plus complexe et peut nécessiter plus de code.
Le code complet est le suivant :
- 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
Il s'agit d'une solution de gestion d'état recommandée par Flutter.Consumer
Utilisé pour lire et surveiller l’état.
Prenons également la minuterie comme exemple.
1. Nous importons d’abord le fournisseur.
fournisseur: ^6.1.2
2. Personnalisez la classe ChangeNotifier.
ChangeNotifier est une classe simple du SDK Flutter. Il est utilisé pour envoyer des notifications aux auditeurs. En d’autres termes, s’il est défini comme ChangeNotifier, vous pouvez vous abonner à ses changements d’état. (Ceci est similaire au modèle d'observateur familier).
Dans le code que nous voulons implémenter, il y a deux variables _counter1 et _counter2. Le code est défini comme suit :
- 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. Utilisez Customer pour envelopper le widget que nous souhaitons actualiser
- 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. Le code complet est le suivant :
- 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,),
- ],
- ),
- ),
- );
- }
- }
Nous pouvons également utiliser GetX pour implémenter une actualisation partielle de l'interface utilisateur.
Installez d'abord GetX :
get: ^4.6.6
Ensuite, nous encapsulons les variables dans GetxController.
- class CounterManagerController extends GetxController {
- final counter1 = 0.obs;
- final counter2 = 0.obs;
- void incrementCount1() {
- counter1.value++;
- }
- void incrementCount2() {
- counter2.value++;
- }
- }
Utilisez ensuite Obx pour envelopper le widget qui doit afficher la logique.
Obx(()=> Text('Numéro du compteur 1 : ${controller.counter2.value}'))
Le code complet est le suivant :
- 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,
- ),
- ],
- ),
- ),
- );
- }
- }
Bien sûr, il existe plusieurs autres façons d'implémenter une actualisation partielle dans GetX. Vous pouvez consulter sa documentation. Voici juste une idée de mise en œuvre.
Les trois méthodes d'implémentation ci-dessus sont toutes implémentées via le framework. Si vous ne souhaitez pas importer ce framework, nous pouvons utiliser GlobalKey pour implémenter la fonction d'actualisation partielle de l'interface utilisateur.
Une clé unique dans toute l'application GlobalKey identifie de manière unique les éléments GlobalKey permet d'accéder aux éléments associés, tels que.BuildContext
.pourStatefulWidgets
, GlobalKey fournit égalementState
Accéder.
Dans notre démo de minuterie, si nous actualisons partiellement l'interface utilisateur via GlobalKey, nous retirons d'abord le widget à actualiser partiellement et l'encapsulons dans un composant distinct.
Le code complet est le suivant. Nous encapsulons le widget pour qu'il soit partiellement actualisé et fournissons une interface pour actualiser les données internes, 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();
- });
- }
- }
Instanciez ensuite GlobaKey dans notre interface principale :
- int _count = 0;
- int _count2 = 0;
- GlobalKey<CounterTextState> textKey = GlobalKey();
- GlobalKey<CounterTextState> textKey2 = GlobalKey();
Dans le cas où l'interface utilisateur doit être actualisée, appelez l'interface fournie à l'étape précédente via GlobalKey et actualisez.
Le code complet est le suivant :
- 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();
- });
- }
- }