Teknologian jakaminen

Useita tapoja Flutterille toteuttaa osittainen päivitys

2024-07-12

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

Sisällysluettelo

Esipuhe

1. Paikallisen virkistyksen tärkeys

1. Konsepti

2. Tärkeys

2. Useita tapoja toteuttaa osittainen päivitys

1. Käytä setState-menetelmää osittaiseen päivitykseen

2. Päivitä käyttöliittymä osittain StatefulWidgetin ja InheritedWidgetin avulla

3.ValueNotifier和ValueListenableBuilder

4. StreamBuilder

5. Palveluntarjoaja

6. HankiX

7. Käytä GlobalKeyä


Esipuhe

Flutterissa tilanhallinta viittaa sovelluksen tietojen tilan hallintaan ja päivittämiseen sekä käyttöliittymän päivittämiseen tilan muutosten mukaan. Tehokas tilanhallinta voi auttaa kehittäjiä luomaan tehokkaampia ja ylläpidettävämpiä sovelluksia.

setState on Flutterin perustavanlaatuisin tilanhallintamenetelmä Kun tila muuttuu, kehys saa ilmoituksen käyttöliittymän uudelleen rakentamisesta. Tietenkin tiedämme, että kun kutsumme setState-menetelmää, sivu piirretään uudelleen Käytä nykyisen sivun käyttöliittymän uudelleenpiirtämiseen.

Mitä menetelmiä Flutterilla on käyttöliittymän osittaiseen päivittämiseen Tässä blogissa on useita tapoja, joilla Flutter voi toteuttaa osittaisen päivityksen?

1. Paikallisen virkistyksen tärkeys

1. Konsepti

Osittainen päivitys tarkoittaa vain osan käyttöliittymän päivittämistä, ei koko sivua. Tämä parantaa suorituskykyä ja käyttökokemusta.

2. Tärkeys

  1. Vältä tarpeettomia uudelleenpiirroksia ja paranna suorituskykyä
  2. Tarjoa sujuvampi käyttökokemus
  3. Vähennä resurssien kulutusta

2. Useita tapoja toteuttaa osittainen päivitys

1. Käytä setState-menetelmää osittaiseen päivitykseen

setState on yleisimmin käytetty tilanhallintamenetelmä Flutterissa. Sitä käytetään ilmoittamaan tilan muutoksista, mikä johtaa rajapinnan rekonstruointiin.

Kun luomme Flutter-projektin, esimerkki järjestelmän oletusarvoisesti luomasta ajastimesta on esimerkki setState osittaisesta päivityksestä.

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

Kuva 1.setState osittainen päivitys

Kun sivu on suhteellisen yksinkertainen, voit käyttää suoraan setState-menetelmää päivittääksesi käyttöliittymän osittain.

Käyttöskenaariot: yksinkertaiset tilan muutokset, kuten napsautusten määrä, kytkimen tila jne.

Varotoimenpiteet:

  1. Säännölliset setState-kutsut voivat aiheuttaa suorituskykyongelmia
  2. Vältä kutsumasta setStatea rakennusmenetelmässä

2. Päivitä käyttöliittymä osittain StatefulWidgetin ja InheritedWidgetin avulla

        StatefulWidget on osa, jolla on tila,InheritedWidget Käytetään tietojen jakamiseen komponenttipuussa.

Kun meidän on jaettava tietoja, voimme harkita StatefulWidgetin ja InheritedWidgetin osittaista käyttöliittymän päivittämistä.

Täydellinen koodi on seuraava:

Kuva 2. Päivitä käyttöliittymä jakamalla tietoja

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

Tämän menetelmän tärkeimmät käyttöskenaariot ovat seuraavat: kun jaetaan tilaa komponenttipuussa, kuten teema, kieliasetukset jne.

Etuna on, että tiedon jakaminen on kätevää ja koodin käyttöönotto

Haittana on, että sen käyttö on monimutkaista ja suorituskyky saattaa heikentyä.

3.ValueNotifier和ValueListenableBuilder

        ValueNotifier Se on yksinkertainen tilanhallintatyökalu,ValueListenableBuilder seurantaa vartenValueNotifier Muutos.

Käyttö on myös hyvin yksinkertaista:

1. Luo ValueNotifier

2. Valvottava Widget-objekti kääritään ValueListenableBuilderiin

3. Tapahtuman käynnistämien tietojen muuttaminen

Edellisiin menetelmiin verrattuna tämä menetelmä on erittäin yksinkertainen ja helppokäyttöinen, ja sen suorituskyky on myös erittäin korkea.

Haitat: Pystyy käsittelemään vain yksinkertaisia ​​tilamuutoksia

Täydellinen koodi on seuraava:

  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 on objekti, jota käytetään asynkronisten tapahtumien toimittamiseen, ja tapahtumia voidaan lähettää StreamControllerin kautta. Jos sinun on päivitettävä käyttöliittymä, voit lähettää tapahtuman Streamiin ja sitten kuunnella StreamBuilderia Kun uusi tapahtuma vastaanotetaan, StreamBuilder rakentaa käyttöliittymän automaattisesti. Tämä menetelmä sopii tilanteisiin, joissa on seurattava useita asynkronisia tapahtumia.

Kun meidän on käsiteltävä asynkronisia tietovirtoja, kuten verkkopyyntöjä, reaaliaikaisia ​​tietoja jne., voimme harkita StreamBuilderin käyttöä. Esimerkiksi seuraavassa esimerkissä kirjoitimme asynkronisen menetelmän, joka simuloi verkkopyyntöä Kun verkkopyyntö ei palauta oikeaa tulosta, voimme ladata edistymispalkin.

Tämän menetelmän etuna on, että se pystyy hallitsemaan asynkronisia pyyntöjä tarkemmin, kuten verkkopyyntöjen tilaa jne. Dior on kuitenkin monimutkaisempi ja saattaa vaatia enemmän koodia.

Täydellinen koodi on seuraava:

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

       Provider Se on Flutterin suosittelema tilanhallintaratkaisu.Consumer Käytetään tilan lukemiseen ja seurantaan.

Otetaan myös ajastin esimerkkinä.

1. Tuomme ensin Palveluntarjoajan.

palveluntarjoaja: ^6.1.2

2. Mukauta ChangeNotifier-luokka.

ChangeNotifier on yksinkertainen luokka Flutter SDK:ssa. Sitä käytetään ilmoitusten lähettämiseen kuulijoille. Toisin sanoen, jos se määritellään ChangeNotifieriksi, voit tilata sen tilamuutokset. (Tämä on samanlainen kuin tuttu tarkkailijamalli).

Koodissa, jonka haluamme toteuttaa, on kaksi muuttujaa _counter1 ja _counter2. Koodi määritellään seuraavasti:

  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. Kääri päivitettävä widget-toiminto Asiakas-painikkeella

  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. Täydellinen koodi on seuraava:

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

Voimme myös käyttää GetX:ää käyttöliittymän osittaisen päivityksen toteuttamiseen.

Asenna GetX ensin:

get: ^4.6.6

Sitten kapseloimme muuttujat GetxControlleriin.

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

Käytä sitten Obxia paketoidaksesi widgetin, jonka on näytettävä logiikkaa.

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

Täydellinen koodi on seuraava:

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

Tietenkin on useita muita tapoja toteuttaa osittainen päivitys GetX:ssä. Voit katsoa sen dokumentaatiota. Tässä on vain yksi toteutusidea.

7. Käytä GlobalKeyä

Yllä olevat kolme toteutustapaa on toteutettu kehyksen kautta. Jos et halua tuoda tätä kehystä, voimme käyttää GlobalKeyä käyttöliittymän osittaisen päivitystoiminnon toteuttamiseen.

Avain, joka on ainutlaatuinen koko sovelluksessa, GlobalKey tunnistaa yksilöllisesti elementit, jotka tarjoavat pääsyn niihin liittyviin elementteihin, kutenBuildContext .vartenStatefulWidgets, GlobalKey tarjoaa myösStatePääsy.

Jos ajastindemossamme päivitämme käyttöliittymän osittain GlobalKeyn kautta, otamme ensin widgetin ulos osittaista päivitystä varten ja kapseloimme sen erilliseksi komponentiksi.

Täydellinen koodi on seuraava. Kapseloimme widgetin osittain päivitettäväksi ja tarjoamme käyttöliittymän sisäisten tietojen päivittämiseen, 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. }

Instantoi sitten GlobaKey pääliittymässämme:

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

Jos käyttöliittymä on päivitettävä, soita edellisessä vaiheessa annettuun käyttöliittymään GlobalKeyn kautta ja päivitä.

Täydellinen koodi on seuraava:

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