Flutter MVVM+provider的基本示例

发布于:2025-08-31 ⋅ 阅读:(15) ⋅ 点赞:(0)

效果图
在这里插入图片描述

provider的基本使用

暴露创建的对象。

void main() {
  runApp(
    MultiProvider(
      providers: [
        Provider(create: (context) => WordRepositoryRemote()),
        ChangeNotifierProvider(
          create: (context) => WordViewModel(context.read()),
        ),
      ],
      child: MyPage(),
    ),
  );
}

MultiProvider声明多个provider,Provider暴露一个对象,ChangeNotifierProvider暴露一个继承ChangeNotifier的对象。
如何获取暴露的对象。

 context.watch<T>(),widget 能够监听到 T 类型的 provider 发生的改变。
 context.read<T>(),直接返回 T,不会监听改变。
 context.select<TR>(R cb(T value)),允许 widget 只监听 T 上的一部分内容的改变。

例子

 wordViewModel = context.read<WordViewModel>();

监听viewmodel更新ui。
当WordViewModel数据有变化并调用notifyListeners()时更新ui。

      body: Consumer<WordViewModel>(
        builder: (context, wordViewModel, child) {
          if (wordViewModel.isLoading) {//加载中
           ........
          } else if (wordViewModel.error != null) {//错误
           .......
          } else {//数据处理与显示
   			.........
          }
        },
      ),

MVVM

model层
模拟一个从网络获取单词数据,延迟2s。
模型

class WordModel {

  String random;
  String def;
  String pron;
  String wordAudio;
  String word;

  WordModel(this.random, this.def, this.pron, this.wordAudio, this.word);
}

从网络获取

import 'package:first_flutter/model/word_model.dart';

class WordRepositoryRemote {
  ///模拟网络请求
  Future<List<WordModel>> getWordList() async {

    List<WordModel> wordList = [];
    await Future.delayed(Duration(seconds: 2));
    WordModel wordModel1 = WordModel(
      "0",
      "抛弃,遗弃;中止;陷入,沉湎于",
      " əˈbændən",
      "http://dict.youdao.com/dictvoice?audio=abandon",
      "abandon",
    );
    WordModel wordModel2 = WordModel(
      "1",
      "抛弃;放纵",
      " əˈbændənmənt",
      "http://dict.youdao.com/dictvoice?audio=abandonment",
      "abandonment",
    );
    WordModel wordModel3 = WordModel(
      "2",
      "缩略词,缩写形式;缩略",
      " əˌbriːviˈeɪʃn",
      "http://dict.youdao.com/dictvoice?audio=abbreviation",
      "abbreviation",
    );
    wordList.add(wordModel1);
    wordList.add(wordModel2);
    wordList.add(wordModel3);
    return wordList;
  }
}

viewmodel
创建WordViewModel类继承ChangeNotifier。

import 'package:first_flutter/model/word_repository_remote.dart';
import 'package:flutter/foundation.dart';

import '../model/word_model.dart';

class WordViewModel extends ChangeNotifier {
  WordRepositoryRemote wordRepositoryRemote;

  List<WordModel> wordList = [];

  bool _isLoading = true;
  String? _error;

  WordViewModel(this.wordRepositoryRemote);

  bool get isLoading => _isLoading;

  //请求单词数据
  void getWordList() async {
    _isLoading = true;
    _error = null;
    notifyListeners();

    try {
      wordList = await wordRepositoryRemote.getWordList();
    } catch (e) {
      _error = e.toString();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
  String? get error => _error;
}

上面代码中notifyListeners调用后,就会调用view层的Consumer< WordViewModel >。
view

import 'package:first_flutter/model/word_repository_remote.dart';
import 'package:first_flutter/view_model/word_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'model/word_model.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        Provider(create: (context) => WordRepositoryRemote()),
        ChangeNotifierProvider(
          create: (context) => WordViewModel(context.read()),
        ),
      ],
      child: MyPage(),
    ),
  );
}

class MyPage extends StatelessWidget {
  const MyPage({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(theme: ThemeData(), home: WordWidget());
  }
}

class MyState extends State {
  late WordViewModel wordViewModel;

  
  void initState() {
    super.initState();
    wordViewModel = context.read<WordViewModel>();
    Future.microtask(() {
      if(mounted){
        wordViewModel.getWordList();
      }
    },);
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("单词 "), centerTitle: true),
      body: Consumer<WordViewModel>(
        builder: (context, wordViewModel, child) {
          if (wordViewModel.isLoading) {
            return Container(
              color: Colors.white,
              child: Center(child: CircularProgressIndicator()),
            );
          } else if (wordViewModel.error != null) {
            return Center(child: Text('错误: ${wordViewModel.error}'));
          } else {
            if (wordViewModel.wordList.isEmpty) {
              return Center(child: Text("暂无数据"));
            } else {
              List<Widget> wlist = [];
              for (int i = 0; i < wordViewModel.wordList.length; i++) {
                WordModel wm = wordViewModel.wordList[i];
                ListTile listTile = ListTile(
                  title: Text(wm.word),
                  subtitle: Text(wm.def),
                );
                wlist.add(listTile);
              }
              return ListView(children: wlist);
            }
          }
        },
      ),
    );
  }
}

class WordWidget extends StatefulWidget {
  
  State<StatefulWidget> createState() {
    return MyState();
  }
}

Future.microtask 的作用就是将一个异步任务调度到微任务队列中,使其在当前同步代码执行结束后、事件队列中的下一个任务被执行之前得到执行。mounted 表示当前 State 对象是否仍然与一个有效的 Widget 关联。除了使用Future.microtask之外,还可以使用

  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (!mounted) return;
    wordViewModel.getWordList();
  });

WidgetsBinding.instance.addPostFrameCallback调用时机,在一帧绘制完成之后、下一帧开始之前立即调用。


网站公告

今日签到

点亮在社区的每一天
去签到