Berbagi teknologi

Beberapa cara Flutter menerapkan penyegaran sebagian

2024-07-12

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

Daftar isi

Kata pengantar

1. Pentingnya penyegaran lokal

1. Konsep

2. Pentingnya

2. Beberapa cara untuk menerapkan penyegaran sebagian

1. Gunakan metode setState untuk penyegaran sebagian

2. Gunakan StatefulWidget dan InheritedWidget untuk menyegarkan sebagian UI

3.ValueNotifier dan ValueListenableBuilder

4.Pembangun Aliran

5.Penyedia

6.DapatkanX

7. Gunakan GlobalKey


Kata pengantar

Di Flutter, manajemen status mengacu pada cara mengelola dan memperbarui status data dalam aplikasi, serta memperbarui UI sesuai dengan perubahan status. Manajemen status yang efektif dapat membantu pengembang menciptakan aplikasi yang lebih efisien dan mudah dipelihara.

setState adalah metode pengelolaan status paling dasar di Flutter. Saat status berubah, framework akan diberi tahu untuk membangun kembali UI. Tentu saja kita tahu bahwa ketika kita memanggil metode setState, halaman akan digambar ulang. Ketika tata letak halaman lebih kompleks, terkadang kita hanya perlu memperbarui UI terpisah. Saat ini, jika kita menggunakan metode setState, itu sudah cukup kinerja yang lebih besar. Gunakan untuk menggambar ulang UI halaman saat ini.

Jadi metode apa yang ada di Flutter untuk menyegarkan sebagian UI? Blog ini mencantumkan beberapa cara Flutter menerapkan penyegaran sebagian.

1. Pentingnya penyegaran lokal

1. Konsep

Penyegaran sebagian mengacu pada penyegaran hanya sebagian antarmuka, bukan keseluruhan halaman. Ini meningkatkan kinerja dan pengalaman pengguna.

2. Pentingnya

  1. Hindari penggambaran ulang yang tidak perlu dan tingkatkan kinerja
  2. Memberikan pengalaman pengguna yang lebih lancar
  3. Mengurangi konsumsi sumber daya

2. Beberapa cara untuk menerapkan penyegaran sebagian

1. Gunakan metode setState untuk penyegaran sebagian

setState adalah metode manajemen status yang paling umum digunakan di Flutter. Metode ini digunakan untuk memberi tahu framework tentang perubahan status, yang mengarah ke rekonstruksi antarmuka.

Saat kita membuat proyek Flutter, contoh timer yang dihasilkan oleh sistem secara default adalah contoh penyegaran parsial 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. }

Gambar 1.setState penyegaran sebagian

Jika halamannya relatif sederhana, Anda dapat langsung menggunakan metode setState untuk menyegarkan sebagian UI.

Skenario penggunaan: perubahan status sederhana, seperti jumlah klik tombol, status peralihan, dll.

Tindakan pencegahan:

  1. Panggilan yang sering ke setState dapat menyebabkan masalah kinerja
  2. Hindari memanggil setState dalam metode build

2. Gunakan StatefulWidget dan InheritedWidget untuk menyegarkan sebagian UI

        StatefulWidget adalah komponen dengan negara,InheritedWidget Digunakan untuk berbagi data dalam pohon komponen.

Saat kami perlu berbagi data, kami dapat mempertimbangkan StatefulWidget dan InheritedWidget untuk menyegarkan sebagian UI.

Kode lengkapnya adalah sebagai berikut:

Gambar 2. Refresh UI dengan berbagi data

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

Skenario penggunaan utama metode ini adalah sebagai berikut: saat berbagi status di pohon komponen, seperti tema, pengaturan bahasa, dll.

Keuntungannya adalah berbagi data menjadi mudah dan pengenalan kode

Kerugiannya adalah penggunaannya rumit dan kinerjanya mungkin terpengaruh.

3.ValueNotifier dan ValueListenableBuilder

        ValueNotifier Ini adalah alat manajemen status sederhana,ValueListenableBuilder untuk pemantauanValueNotifier Perubahan.

Penggunaannya juga sangat sederhana:

1. Buat instance ValueNotifier

2. Objek Widget yang akan dipantau dibungkus dengan ValueListenableBuilder

3. Cara mengubah data yang dipicu peristiwa

Dibandingkan dengan cara-cara sebelumnya, cara ini sangat sederhana dan mudah digunakan, serta performanya juga sangat tinggi.

Kekurangan: Hanya dapat menangani perubahan keadaan sederhana

Kode lengkapnya adalah sebagai berikut:

  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.Pembangun Aliran

Stream adalah objek yang digunakan untuk mengirimkan kejadian asinkron, dan kejadian dapat dikirim melalui StreamController. Jika Anda perlu menyegarkan UI, Anda dapat mengirim peristiwa ke Aliran, lalu menggunakan StreamBuilder untuk mendengarkan Aliran. Saat peristiwa baru diterima, StreamBuilder akan secara otomatis membangun kembali UI. Metode ini cocok untuk situasi di mana beberapa kejadian asinkron perlu dipantau.

Saat kita perlu memproses aliran data asinkron, seperti permintaan jaringan, data real-time, dll., kita dapat mempertimbangkan untuk menggunakan StreamBuilder. Misalnya, dalam contoh berikut, kami menulis metode asinkron yang menyimulasikan permintaan jaringan. Ketika permintaan jaringan tidak memberikan hasil yang benar, kami dapat memuat bilah kemajuan.

Keuntungan metode ini adalah dapat mengontrol permintaan asinkron dengan lebih akurat, seperti status permintaan jaringan, dll. Namun, Dior lebih kompleks dan mungkin memerlukan lebih banyak kode.

Kode lengkapnya adalah sebagai berikut:

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

       Provider Ini adalah solusi manajemen negara yang direkomendasikan oleh Flutter.Consumer Digunakan untuk membaca dan memantau status.

Mari kita ambil juga pengatur waktu sebagai contoh.

1. Pertama kita import Provider.

penyedia: ^6.1.2

2. Sesuaikan kelas ChangeNotifier.

ChangeNotifier adalah kelas sederhana di Flutter SDK. Ini digunakan untuk mengirim pemberitahuan kepada pendengar. Dengan kata lain, jika didefinisikan sebagai ChangeNotifier, Anda dapat berlangganan perubahan statusnya. (Ini mirip dengan pola pengamat yang lazim).

Dalam kode yang ingin kita implementasikan, ada dua variabel _counter1 dan _counter2. Kode tersebut didefinisikan sebagai berikut:

  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. Gunakan Pelanggan untuk membungkus Widget yang ingin kita segarkan

  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. Kode lengkapnya adalah sebagai berikut:

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

Kita juga dapat menggunakan GetX untuk mengimplementasikan penyegaran sebagian UI.

Instal pertama GetX:

get: ^4.6.6

Kemudian kami merangkum variabel di 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. }

Kemudian gunakan Obx untuk membungkus Widget yang perlu menampilkan logika.

Obx(()=&gt; Teks('Nomor penghitung 1: ${controller.counter2.value}'))

Kode lengkapnya adalah sebagai berikut:

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

Tentu saja, ada beberapa cara lain untuk menerapkan penyegaran sebagian di GetX. Anda dapat melihat dokumentasinya. Ini hanyalah satu ide implementasi.

7. Gunakan GlobalKey

Ketiga metode implementasi di atas semuanya diimplementasikan melalui framework. Jika Anda tidak ingin mengimpor framework ini, kita dapat menggunakan GlobalKey untuk mengimplementasikan fungsi penyegaran sebagian pada UI.

Kunci yang unik di seluruh aplikasi GlobalKey secara unik mengidentifikasi elemen. GlobalKey menyediakan akses ke elemen terkait, sepertiBuildContext .untukStatefulWidgets, GlobalKey juga menyediakanStateMengakses.

Dalam demo pengatur waktu kami, jika kami me-refresh sebagian UI melalui GlobalKey, pertama-tama kami mengeluarkan Widget untuk di-refresh sebagian dan merangkumnya ke dalam komponen terpisah.

Kode lengkapnya adalah sebagai berikut. Kami merangkum Widget untuk disegarkan sebagian dan menyediakan antarmuka untuk menyegarkan data internal, 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. }

Kemudian buat instance GlobaKey di antarmuka utama kami:

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

Jika UI perlu disegarkan, panggil antarmuka yang disediakan pada langkah sebelumnya melalui GlobalKey dan segarkan.

Kode lengkapnya adalah sebagai berikut:

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