You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中无法在Main.dart及UI代码正确使用SharedPreferences的问题

问题解决:SharedPreferences全局服务优化与UI异步处理

核心问题分析

  1. 重复获取SharedPreferences实例:你的getSharedPrefs()每次调用都会重新执行SharedPreferences.getInstance(),就算main里提前await了,每次调用业务方法还是会重新发起实例请求,导致提前初始化的操作完全无效。
  2. UI中无法直接处理异步方法:Flutter的UI渲染是同步流程,不能直接在build方法里使用await,必须通过异步组件或状态管理来处理Future类型的数据。

解决方案步骤

1. 改造存储服务,缓存SharedPreferences实例

修改KeyValueStorageServiceImpl,把SharedPreferences实例保存为类成员,确保只初始化一次:

import 'key_value_class.dart';
import 'package:shared_preferences/shared_preferences.dart';

class KeyValueStorageServiceImpl extends KeyValueStorageService {
  // 缓存SharedPreferences实例,避免重复初始化
  SharedPreferences? _prefs;

  // 全局初始化方法,仅需调用一次
  Future<void> init() async {
    _prefs ??= await SharedPreferences.getInstance();
  }

  // 私有方法,确保实例已完成初始化
  SharedPreferences get _instance {
    if (_prefs == null) {
      throw StateError('请先调用init()初始化KeyValueStorageService');
    }
    return _prefs!;
  }

  @override
  Future<T?> getValue<T>(String key) async {
    final prefs = _instance;
    switch (T) {
      case String:
        return prefs.getString(key) as T?;
      case int:
        return prefs.getInt(key) as T?;
      case double:
        return prefs.getDouble(key) as T?;
      case bool:
        return prefs.getBool(key) as T?;
      case List:
        return prefs.getStringList(key) as T?;
      default:
        throw UnimplementedError('未实现${T.runtimeType}类型的读取逻辑');
    }
  }

  @override
  Future<bool> removeKey(String key) async {
    return _instance.remove(key);
  }

  @override
  Future<void> setKeyValue<T>(String key, T value) async {
    final prefs = _instance;
    switch (T) {
      case String:
        await prefs.setString(key, value as String);
        break;
      case int:
        await prefs.setInt(key, value as int);
        break;
      case double:
        await prefs.setDouble(key, value as double);
        break;
      case bool:
        await prefs.setBool(key, value as bool);
        break;
      case List:
        await prefs.setStringList(key, value as List<String>);
        break;
      default:
        throw UnimplementedError('未实现${T.runtimeType}类型的写入逻辑');
    }
  }
}

2. 修改main.dart的初始化逻辑

创建全局单例实例,初始化后注入到ProviderScope供全局调用:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Enviroment.initEnvirontment();
  
  // 创建存储服务实例并完成初始化
  final storageService = KeyValueStorageServiceImpl();
  await storageService.init();
  
  await initializeDateFormatting('es_ES', null);
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) => runApp(ProviderScope(
            overrides: [
              // 注入单例,全局可通过ref获取
              Provider<KeyValueStorageService>((ref) => storageService),
            ],
            child: MyApp(),
          )));
}

3. UI中处理异步数据的两种可行方式

方式一:使用FutureBuilder直接渲染

在UI组件中用FutureBuilder包裹需要异步数据的部分:

class UserInfoWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final storageService = ref.watch(keyValueStorageServiceProvider);
    return FutureBuilder<String?>(
      future: storageService.getValue<String>('user'),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        }
        final user = snapshot.data;
        return Text(user ?? '未登录');
      },
    );
  }
}

方式二:用Riverpod预加载并缓存数据

创建专门的FutureProvider缓存用户数据,避免UI重建时重复发起请求:

// 定义全局Provider缓存用户数据
final userProvider = FutureProvider<String?>((ref) async {
  final storageService = ref.watch(keyValueStorageServiceProvider);
  return storageService.getValue<String>('user');
});

// UI组件中使用
class UserInfoWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider);
    return userAsync.when(
      loading: () => CircularProgressIndicator(),
      error: (err, stack) => Text('加载失败'),
      data: (user) => Text(user ?? '未登录'),
    );
  }
}

关键说明

  • 缓存实例:改造后init()仅执行一次SharedPreferences实例初始化,后续所有业务方法复用该实例,既提升性能又确保提前初始化的效果生效。
  • UI异步处理:Flutter的UI渲染是同步流程,必须通过FutureBuilder或状态管理库的异步Provider来处理Future类型数据,不能直接在build方法内使用await。

内容的提问来源于stack exchange,提问作者Rudy Rosa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 07:01:00