Flutter:如何用stacked包实现Future列表刷新后显示小计
使用Stacked实现该场景的最佳方案
完全不需要为小计单独创建ViewModel,直接在现有的FutureViewModel里处理即可——毕竟小计是商品列表的衍生数据,放在同一个ViewModel中维护逻辑更连贯。以下是具体实现步骤和代码示例:
1. 编写ViewModel逻辑
核心思路是在ViewModel中维护选中日期、商品列表,同时通过计算属性或方法自动生成小计,利用Stacked的响应式能力通知UI更新:
import 'package:stacked/stacked.dart'; import 'package:your_app/models/product.dart'; import 'package:your_app/services/product_service.dart'; class ProductListViewModel extends FutureViewModel<List<Product>> { final ProductService _productService = ProductService(); DateTime _selectedDate = DateTime.now(); // 用@observable标记,让Stacked感知变量变化 @observable List<Product> _products = []; DateTime get selectedDate => _selectedDate; // 用@computed自动计算小计,每次_products变化时自动更新 @computed double get subtotal => _products.fold( 0.0, (total, product) => total + product.price, ); // 日期变更时重新获取数据 void updateDate(DateTime newDate) { _selectedDate = newDate; // 重新触发Future获取新数据 setFuture(_fetchProductsByDate()); } @override Future<List<Product>> futureToRun() => _fetchProductsByDate(); Future<List<Product>> _fetchProductsByDate() async { final fetchedProducts = await _productService.getProducts(_selectedDate); _products = fetchedProducts; return fetchedProducts; } }
2. 实现UI层
通过ViewModelBuilder绑定ViewModel,在AppBar中直接读取subtotal,日期选择器触发updateDate方法即可:
import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:your_app/viewmodels/product_list_viewmodel.dart'; import 'package:your_app/models/product.dart'; class ProductListView extends StatelessWidget { const ProductListView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ViewModelBuilder<ProductListViewModel>.reactive( viewModelBuilder: () => ProductListViewModel(), onModelReady: (model) => model.fetchData(), builder: (context, model, child) { return Scaffold( appBar: AppBar( title: const Text("商品列表"), actions: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( "小计: ¥${model.subtotal.toStringAsFixed(2)}", style: const TextStyle(fontSize: 16), ), ), ], ), body: model.isBusy ? const Center(child: CircularProgressIndicator()) : Column( children: [ // 日期选择按钮 ElevatedButton( onPressed: () async { final pickedDate = await showDatePicker( context: context, initialDate: model.selectedDate, firstDate: DateTime(2020), lastDate: DateTime.now(), ); if (pickedDate != null) { model.updateDate(pickedDate); } }, child: Text("选择日期: ${model.selectedDate.toString().split(' ')[0]}"), ), // 商品列表 Expanded( child: ListView.builder( itemCount: model.data?.length ?? 0, itemBuilder: (context, index) { final product = model.data![index]; return ListTile( title: Text(product.name), trailing: Text("¥${product.price.toStringAsFixed(2)}"), ); }, ), ), ], ), ); }, ); } }
关键说明
- 利用
@observable和@computed实现响应式:商品列表更新时,小计会自动重新计算,UI也会同步刷新 - 无需额外ViewModel:小计属于商品列表的附属数据,放在同一个ViewModel中能避免状态分散,逻辑更清晰
- 复用FutureViewModel的状态管理:直接用
setFuture重新触发数据请求,自动处理加载、错误等状态
内容的提问来源于stack exchange,提问作者Meysam
相关产品推荐
相关产品推荐

