flutter之状态管理

我爱海鲸 2024-08-02 16:07:38 flutter学习

简介Dart、跨平台、provider

1、搭建flutter的开发环境:http://www.haijin.xyz/article/730

2、创建一个项目:

flutter create myapp

3、项目结构:

4、相关代码:

about_tab_page.dart:

import 'package:flutter/material.dart';
import 'package:myapp/api/wallpaper_detail_api.dart';
import 'package:myapp/model/wallpaper_detail.dart';
import 'package:myapp/model/wallpaper_type.dart';
import 'package:myapp/pages/detail_page.dart';
import 'package:myapp/route/routes.dart';
import 'package:provider/provider.dart';

import '../../vm/search_vm.dart';
import '../about_page.dart';


class AboutTabPage extends StatefulWidget {
  final Data? data;

  final SearchVm searchVm;

  const AboutTabPage({
    super.key,
    required this.data,
    required this.searchVm,
  });



  @override
  _HomePageState createState() => _HomePageState(data: data,searchVm:searchVm);
}

class _HomePageState extends State<AboutTabPage> with AutomaticKeepAliveClientMixin {

  final Data? data;

  late final SearchVm searchVm;

  _HomePageState({
    required this.data,
    required this.searchVm,
  });

  final ScrollController _scrollController = ScrollController();

  // 索引
  int _itemIndex = 0;

  @override
  void didUpdateWidget(covariant AboutTabPage oldWidget) {
    super.didUpdateWidget(oldWidget);
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<SearchVm>(create: (content){
      return searchVm;
    },child: Consumer<SearchVm>(builder:(context,vm,child){
      return     Scaffold(
        body: RefreshIndicator(
          onRefresh: vm.refreshFunc, // 下拉刷新
          child: vm.wallpaper == null
              ? Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    setState(() {
                      vm.currentPage = 1; // 重置页码
                      // _records.clear(); // 清空列表
                    });
                    vm.fetchData(data: data); // 获取第一页数据
                  },
                  child: Text('获取壁纸'),
                ),
              ],
            ),
          ) : ListView.builder(
            itemCount: (vm.wallpaper?.pageSize??0) as int,
            itemBuilder: (context, index) {
              _itemIndex = index;
              if(index > vm.currentPage * 10 - 5){
                print("加载更多");
                vm.currentPage++;
                vm.fetchData(data: data);
              }
              if (index < vm.records!.length) {
                final item = vm.records?[index];
                return ListTile(
                  leading: Image.network(item!.wallpaperListUrl??""),
                  title: Text(item!.wallpaperName??""),
                  subtitle: Text('ID: ${item?.id}'),
                  onTap: () {
                    print("跳转到壁纸的详情页面");
                    Navigator.pushNamed(context, RoutePath.detailPage,
                        arguments:item
                    );
                    // Navigator.push(context, MaterialPageRoute(builder: (context){
                    //   return DetailPage(
                    //     records: item,
                    //   );
                    // }));
                  },
                );
              } else {
                return Container(
                  height: 50,
                  alignment: Alignment.center,
                  child: CircularProgressIndicator(),
                );
              }

            },
            // controller: _scrollController,
          ),
        ),
      );
    }),);
  }

  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
}

about_page.dart:

// lib/pages/about_page.dart
import 'package:flutter/material.dart';
import 'package:myapp/api/wallpaper_type_api.dart';
import 'package:myapp/model/wallpaper_type.dart';
import 'package:myapp/pages/about/about_tab_page.dart';
import 'package:myapp/vm/search_vm.dart';

class AboutPage extends StatefulWidget {
  const AboutPage({super.key});

  @override
  State<StatefulWidget> createState() {
    return _AboutPageState();
  }
}

class _AboutPageState extends State<AboutPage> with SingleTickerProviderStateMixin {


  WallpaperTypeApi _wallpaperTypeApi = WallpaperTypeApi();

  late TabController _tabController;

  WallpaperType? _wallpaperType;

  final List<Tab> _tabs = [];

  bool _isDataLoaded = false; // 添加一个标志来跟踪数据是否加载完成

  late List<Widget> _tabBarView = [];

  String keyWorld = "";

  final TextEditingController _textEditingController = TextEditingController();

  final SearchVm _searchVm = SearchVm();


  Future<void> _fetchData() async {
    try{
      dynamic data = await _wallpaperTypeApi.awaitFetchTodoData();
      _wallpaperType = WallpaperType.fromJson(data);
      print("获取类型的数据");
      print(_wallpaperType?.message);
      setState(() {
        if (_wallpaperType?.code == 0) {
          _tabs.add(Tab(text: "搜索"));
          _wallpaperType?.data?.forEach((item){
            _tabs.add(Tab(text: item.typeName));
          });
        }
        _tabController = TabController(length: _tabs.length, vsync: this);
        _tabController.addListener(_handleTabChange);

        _tabBarView = getTabBarView();
        _isDataLoaded = true;
      });
    } catch(e) {
      print(e);
    }
  }


  void _handleTabChange() {
      // 当前选中的标签索引
      final int currentIndex = _tabController.index;
      print('Selected tab: $currentIndex');
      // 在这里可以执行与当前选中标签相关的逻辑
  }


@override
  void initState() {
    super.initState();
    _fetchData();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Container(
          height: 45,
          decoration: BoxDecoration(
            border: Border(
              top: BorderSide(width: 0.5,color: Colors.grey),
              bottom: BorderSide(width: 0.5,color: Colors.grey),
            )
          ),
          padding: EdgeInsets.only(left:20,right: 20),
          child: Row(children: [
            // Text("搜索热词",style: TextStyle(fontSize: 14,color: Colors.black),),
            Container(child: _inputBox),
            Expanded(child: SizedBox()),
            GestureDetector(onTap: (){
              keyWorld = _textEditingController.text;
              _searchVm.setKeyWorld(keyWorld);
              // 跳转到搜索页
              _tabController.animateTo(0, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
            },child: Image.asset("images/search.png",width: 30,height: 30,),),
          ],),
        ),

        bottom: _isDataLoaded ? TabBar( // 使用 _isDataLoaded 标志来决定是否显示 TabBar
          controller: _tabController,
          tabs: _tabs,
          labelColor: Colors.blue,
          indicatorColor: Colors.blue,
          isScrollable: true,
        ) : null,
      ),

      body: SafeArea(child:
      _isDataLoaded ? TabBarView(controller: _tabController,children: _tabBarView,) : const Center(child: Text("关于页面"),),
      ),
    );
  }


  List<Widget> getTabBarView() {
    List<Widget>? tbs = [];
    Data allData = Data(id: null,typeName: "所有");
    tbs.add(AboutTabPage(data: allData,searchVm: _searchVm,));
    _wallpaperType?.data?.forEach((item){
      SearchVm s = SearchVm();
      tbs.add(AboutTabPage(data: item,searchVm: s,));
    });
    return tbs??[];
  }


  Widget get _inputBox {
    return Expanded(
      child: TextField(
        controller: _textEditingController,
        style: const TextStyle(

            fontSize: 18.0, color: Colors.black, fontWeight: FontWeight.w300),
        decoration: InputDecoration(
//                   contentPadding: EdgeInsets.fromLTRB(1, 3, 1, 3),
//                   contentPadding: EdgeInsets.only(bottom: 0),
            contentPadding:
            const EdgeInsets.symmetric(vertical: 0, horizontal: 12),
            border: InputBorder.none,
            hintText: "搜你想搜的",
            hintStyle: TextStyle(fontSize: 15),
            enabledBorder: const OutlineInputBorder(
              // borderSide: BorderSide(color: Color(0xFFDCDFE6)),
              borderSide: BorderSide(color: Colors.transparent),
              borderRadius: BorderRadius.all(Radius.circular(4.0)),
            ),
            focusedBorder: const OutlineInputBorder(
                borderRadius: BorderRadius.all(Radius.circular(8)),
                borderSide: BorderSide(color: Colors.transparent))),
      ),
    );
    ;
  }

}

search_vm.dart:

import 'package:flutter/foundation.dart';
import 'package:myapp/pages/about/about_tab_page.dart';

import '../api/wallpaper_detail_api.dart';
import '../model/wallpaper_detail.dart';
import '../model/wallpaper_type.dart';

class SearchVm with ChangeNotifier {
  String keyWord = "";

  Data? _data;

  final WallpaperDetailApi _wallpaperDetailApi = WallpaperDetailApi();

  WallpaperDetail? wallpaper;

  List<Records> records = [];
  // 页码
  int currentPage = 1;

  void setKeyWorld(String key){
    keyWord = key;
    print("搜索的关键词:$key");
    currentPage = 1;
    fetchData(data: _data);
  }

  Future<void> fetchData({Data? data}) async {
    try{
      print("页码数:");
      print(currentPage);
      _data = data;
      var id = null;
      if (data != null && data.id != null) {
        id = data.id as int;
      }
      _wallpaperDetailApi.fetchTodoData(currentPage: currentPage,callBack: _analyzeWallpaperData,typeId: id,name: keyWord);

    } catch(e) {
      print(e);
    }

  }

  // 拉下刷新函数
  Future<void> refreshFunc({Data? data}) async {
    print("刷新");
    currentPage = 1;
    fetchData(data: data);
  }


  // 解析获取的数据
  void _analyzeWallpaperData(dynamic data) {
    wallpaper = WallpaperDetail.fromJson(data);

    if (currentPage == 1) {
      // 如果是第一页,则替换列表
      records = wallpaper!.records!;
    } else {
      // 如果不是第一页,则追加到列表末尾
      wallpaper!.records?.forEach((item){
        records.add(item);
      });
    }
    notifyListeners();
    print("改变数据");
  }
}

pubspec.yaml:

name: myapp
description: "A new Flutter project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1

environment:
  sdk: '>=3.4.4 <4.0.0'
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.6
  dio: ^4.0.6
  provider: ^6.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0
flutter:
  uses-material-design: true
  assets:
    - images/

状态管理现在依赖的组件就是provider。

过程:首先 在一个vm模型中 with ChangeNotifier

数据请求完之后可以notifyListeners();重新渲染ui,

ChangeNotifierProvider<SearchVm>(create: (content){
      return searchVm;
    },child: Consumer<SearchVm>(builder:(context,vm,child){

在需要重新渲染ui的地方使用ChangeNotifierProvider传入定义好的searchVm ,当searchVm数据发生改变的时候就会通知Consumer进行重新渲染。

这里的例子是一个搜索的功能。

运行截图:

你好:我的2025