flutter存储知识点总结

一、数据存储

1、本地持久化存储SharedPreferences

SharedPreferences为轻量级存储,存储少量简单数据,键值对形式,不适合大量、复杂数据的存储。

dart 复制代码
import 'package:shared_preferences/shared_preferences.dart'; 
// 存储数据 
Future<void> saveData() async { 
// 获取 SharedPreferences 实例 
final prefs = await SharedPreferences.getInstance(); 
// 存储不同类型的数据 await prefs.setString('user_token', 'abc123456'); // 字符串
await prefs.setInt('user_age', 25); // 整数 a
wait prefs.setBool('is_login', true); // 布尔值 
await prefs.setDouble('height', 1.75); // 浮点数 
await prefs.setStringList('hobbies', ['读书', '运动']); // 字符串列表 } 

// 读取数据 Future<void> readData() async { 
final prefs = await SharedPreferences.getInstance(); 
// 读取数据(第二个参数是默认值,避免 null) 
String? token = prefs.getString('user_token') ?? ''; 
int age = prefs.getInt('user_age') ?? 0;
bool isLogin = prefs.getBool('is_login') ?? false; 
print('token: $token, age: $age, isLogin: $isLogin'); 
} 
// 删除数据 
Future<void> removeData() async { 
final prefs = await SharedPreferences.getInstance(); await prefs.remove('user_token'); 
// 删除单个键 
// await prefs.clear(); 
// 清空所有数据 }

2、Provider存储

Provider 是运行时的内存状态管理工具 非本地持久化存储,Provider 存储的数据只在 App 运行时有效,重启后丢失。它的核心价值是让数据在多个 Widget 之间共享、响应式更新,是 "内存级" 的数据存储与共享方案。适用于需要跨组件共享、实时响应更新 的运行时数据。 Provider的原理是基于 Flutter 原生的 InheritedWidget 实现的,而 InheritedWidget 的核心特性就是通过 Context 向上查找共享数据

  • 从 Provider 中读取 / 监听数据 :必须依赖 context(因为要确定查找的上下文范围);

  • 从 Provider 中修改数据 :通常也需要 context,但有替代方案(无需 Context);

  • 初始化 / 注入 Provider:不需要 context(在根节点创建时)。

2.1 定义数据模型
dart 复制代码
import 'package:flutter/foundation.dart';

class ContractDataModel extends ChangeNotifier {
  // ========== 核心数据字段 (仅保留3个示例) ==========

  // 1. 房源类型名称 (来自第一步)
  String _goodsTypeName = '';

  // 2. 租客姓名 (来自第二步)
  String _customerName = '';

  // 3. 租金单价 (来自第三步)
  String _unitPrice = '';

  // ========== Getters ==========
  String get goodsTypeName => _goodsTypeName;
  String get customerName => _customerName;
  String get unitPrice => _unitPrice;

  // ========== 统一更新方法 ==========
  /// 更新核心数据
  /// 只要传入的值不为 null 就更新,允许空字符串覆盖原有数据
  void updateCoreInfo({
    String? goodsTypeName,
    String? customerName,
    String? unitPrice,
  }) {
    if (goodsTypeName != null) _goodsTypeName = goodsTypeName;
    if (customerName != null) _customerName = customerName;
    if (unitPrice != null) _unitPrice = unitPrice;

    // 通知监听者重建 UI
    notifyListeners();
  }

  // ========== 验证数据是否完整 (示例) ==========
  bool validateCoreInfo() {
    if (_goodsTypeName.isEmpty) return false;
    if (_customerName.isEmpty) return false;
    if (_unitPrice.isEmpty) return false;
    return true;
  }

  // ========== 获取所有数据的 Map ==========
  Map<String, dynamic> toMap() {
    return {
      'goodsTypeName': _goodsTypeName,
      'customerName': _customerName,
      'unitPrice': _unitPrice,
    };
  }

  // ========== 清空所有数据 ==========
  void clear() {
    _goodsTypeName = '';
    _customerName = '';
    _unitPrice = '';

    notifyListeners();
  }
}
2.2 更新provider中的数据
ini 复制代码
final contractModel = Provider.of<ContractDataModel>(context, listen: false);
contractModel.updatePersonInfo(
  customerCertificateId: _customerCertificateId,
  customerName: _customerName,
);
2.3 获取provider,提取内部的数据
ini 复制代码
late _contractDataModel = Provider.of<ContractDataModel>(context, listen: false);
if(_contractDataModel.signClientType == '1'){//企业
  _customerCertificateType = 'Z';
}else{
  _customerCertificateType = '';
}

tips: 使用late的作用是什么? late 是用来修饰延迟初始化变量的关键字。

  • late用来声明变量时无法立即赋值的问题,Provider依赖context,而 context 通常在 Widget 的 build 方法、initState的位置才能获取,声明无法获取从而报错。
  • 允许变量非空但延迟复制。(Dart空安全核心)Dart 开启空安全后,未用 ? 标记的变量必须声明时赋值 或用 late 修饰。

二、Provider 流程页面剖析

应该这样理解:

✅ Provider 是在 CreateReserveStepPage 中创建的(第52行)

✅ 三个 Step Widget 各自有自己独立的 context

✅ 但它们都是 ChangeNotifierProvider 的子孙节点

✅ 所以它们都能通过各自的 context 向上查找,找到同一个 Provider 实例

或者说:

✅ 三个 Widget 的 context 都能访问到在 CreateReserveStepPage 中创建的 Provider,因为它们都在 Provider 的子树中。

如下:

php 复制代码
class CreateReserveStepPage extends StatefulWidget {
final Map params;
const CreateReserveStepPage({Key key,this.params}) : super(key: key);
@override
_CreateReserveStepPage createState() => _CreateReserveStepPage();
}

class _CreateReserveStepPage extends State<CreateReserveStepPage> {
final BrnMetaHorizontalStepsManager _stepsManager = BrnMetaHorizontalStepsManager();
int _currentIndex = 0;
bool _isCompleted = false;
Timer _timer;
int _elapsed = 0;
Map<String, dynamic> _contractParams = {};
final List<String> _stepTitles = ['合同信息', '租客/入住人信息', '账单/补充信息'];
// 验证回调函数
Function _validateContractWidget;
Function _validatePersonWidget;
Function _validateBillWidget;
// 保存数据回调函数
Function _savePersonWidget;
Function _saveBillWidget;
// 添加ScrollController
final ScrollController _scrollController = ScrollController();
void initState() {
  super.initState();
}

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

@override
Widget build(BuildContext context) {
  return ChangeNotifierProvider(
    create: (_) => ContractDataModel(),
    builder: (providerContext, child) {
      return Scaffold(
        appBar: BrnAppBar(
          title: '创建签约',
          leading: IconButton(
            icon: Icon(Icons.arrow_back_ios_new, color: Colors.black),
            onPressed: () => BoostUtil.finish(),
          ),
        ),
        body: Column(
          children: [
            _stepsManager.buildSteps(
              steps: _stepTitles.map((title) => BrunoStep(stepContentText: title)).toList(),
              currentIndex: _currentIndex,
              isCompleted: _isCompleted,
            ),
            const SizedBox(height: 24),
            Expanded(
              child: SingleChildScrollView(
                controller: _scrollController,
                child: _buildStepContent(_currentIndex),
              ),
            ),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
              decoration: BoxDecoration(
                color: Colors.white,
                border: Border(top: BorderSide(color: Color(0xFFF0F0F0), width: 1)),
              ),
              child: _buildBottomButtons(providerContext),
            ),
          ],
        ),
      );
    },
  );
}

Widget _buildStepContent(int index) {
  switch (index) {
    case 0:
      return ReservationStepContractWidget(
        params: this.widget.params,
        onDataChanged: _handleContractDataChanged,
        onValidateCallback: (validateFunc) {
          _validateContractWidget = validateFunc;
        },
      );
    case 1:
      return ReservationStepPersonWidget(
        onDataChanged: _handlePersonDataChanged,
        onValidateCallback: (validateFunc) {
          _validatePersonWidget = validateFunc;
        },
        onSaveCallback: (saveFunc) {
          _savePersonWidget = saveFunc;
        },
      );
    case 2:
      return ReservationStepBillWidget(
        onDataChanged: _handleBillDataChanged,
        onValidateCallback: (validateFunc) {
          _validateBillWidget = validateFunc;
        },
        onSaveCallback: (saveFunc) {
          _saveBillWidget = saveFunc;
        },
      );
    default:
      return SizedBox();
  }
}
相关推荐
一个假的前端男2 小时前
# 从零开始创建 Flutter Web 项目(附 VS Code 插件推荐)
前端·flutter·react.js
一个假的前端男3 小时前
[特殊字符] Flutter 安装完整指南 Windows—— 2026最新版
windows·flutter
程序员老刘3 小时前
Flutter版本选择指南:3.41 发布,稳定的开年 | 2026年2月
flutter·客户端
恋猫de小郭3 小时前
Flutter 的真正价值是什么?深度解析再结合鸿蒙,告诉你 Flutter 的真正优势
android·前端·flutter
2501_921930836 小时前
Flutter for OpenHarmony:三方库实战 animations 页面过渡动画详解
flutter
lqj_本人7 小时前
Flutter三方库适配OpenHarmony【apple_product_name】oh-package.json5配置详解
flutter
2501_921930837 小时前
第三方库引入实战指南 Flutter for OpenHarmony:path_provider 文件路径详解
flutter
齐生17 小时前
网络知识点 - TCP/IP 四层模型知识大扫盲
笔记·ios
IT技术分享社区8 小时前
数码资讯:iPhone 18 Pro,十大升级细节浮出水面
ios·手机·iphone