Flutter/Dart应用实现多账户登录及切换、增删功能的方案
实现Flutter/Dart多账户功能(类似Instagram)
1. 定义账户数据模型
先创建UserAccount类,封装账户核心信息,支持序列化用于本地存储:
class UserAccount { final String id; final String username; final String avatarUrl; final String authToken; // 接口请求令牌 UserAccount({ required this.id, required this.username, required this.avatarUrl, required this.authToken, }); // 序列化/反序列化方法 Map<String, dynamic> toJson() => { 'id': id, 'username': username, 'avatarUrl': avatarUrl, 'authToken': authToken, }; factory UserAccount.fromJson(Map<String, dynamic> json) => UserAccount( id: json['id'], username: json['username'], avatarUrl: json['avatarUrl'], authToken: json['authToken'], ); }
2. 本地存储账户数据
用shared_preferences实现账户的持久化存储,封装读写删操作:
import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; class AccountStorage { static const String _accountsKey = 'saved_accounts'; static const String _currentAccountIdKey = 'current_account_id'; // 保存所有账户 static Future<void> saveAccounts(List<UserAccount> accounts) async { final prefs = await SharedPreferences.getInstance(); final accountsJson = accounts.map((acc) => jsonEncode(acc.toJson())).toList(); await prefs.setStringList(_accountsKey, accountsJson); } // 读取所有账户 static Future<List<UserAccount>> getAccounts() async { final prefs = await SharedPreferences.getInstance(); final accountsString = prefs.getStringList(_accountsKey); if (accountsString == null || accountsString.isEmpty) return []; return accountsString .map((str) => UserAccount.fromJson(jsonDecode(str))) .toList(); } // 保存当前选中账户ID static Future<void> saveCurrentAccountId(String accountId) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(_currentAccountIdKey, accountId); } // 获取当前选中账户ID static Future<String?> getCurrentAccountId() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString(_currentAccountIdKey); } // 删除指定账户 static Future<void> deleteAccount(String accountId) async { final accounts = await getAccounts(); final updatedAccounts = accounts.where((acc) => acc.id != accountId).toList(); await saveAccounts(updatedAccounts); // 处理当前账户被删除的情况 final currentId = await getCurrentAccountId(); if (currentId == accountId) { if (updatedAccounts.isNotEmpty) { await saveCurrentAccountId(updatedAccounts.first.id); } else { await prefs.remove(_currentAccountIdKey); } } } }
3. 全局状态管理(Provider)
用ChangeNotifier封装账户状态,提供切换、添加、删除的核心逻辑:
import 'package:flutter/material.dart'; class AccountProvider extends ChangeNotifier { List<UserAccount> _accounts = []; UserAccount? _currentAccount; List<UserAccount> get accounts => _accounts; UserAccount? get currentAccount => _currentAccount; // 初始化加载本地账户数据 Future<void> init() async { _accounts = await AccountStorage.getAccounts(); final currentId = await AccountStorage.getCurrentAccountId(); if (currentId != null) { _currentAccount = _accounts.firstWhere((acc) => acc.id == currentId); } notifyListeners(); } // 添加新账户 Future<void> addAccount(UserAccount newAccount) async { if (!_accounts.any((acc) => acc.id == newAccount.id)) { _accounts.add(newAccount); await AccountStorage.saveAccounts(_accounts); // 首次添加自动设为当前账户 if (_currentAccount == null) { await switchAccount(newAccount.id); } notifyListeners(); } } // 切换账户 Future<void> switchAccount(String accountId) async { _currentAccount = _accounts.firstWhere((acc) => acc.id == accountId); await AccountStorage.saveCurrentAccountId(accountId); notifyListeners(); // 这里可添加切换后的逻辑,比如重置API客户端、清空缓存等 // await ApiClient.resetToken(_currentAccount!.authToken); } // 删除账户 Future<void> removeAccount(String accountId) async { await AccountStorage.deleteAccount(accountId); _accounts = await AccountStorage.getAccounts(); final currentId = await AccountStorage.getCurrentAccountId(); _currentAccount = currentId != null ? _accounts.firstWhere((acc) => acc.id == currentId) : null; notifyListeners(); } }
4. UI实现示例
账户切换面板(类似Instagram的头像弹窗)
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class AccountSwitcher extends StatelessWidget { const AccountSwitcher({super.key}); @override Widget build(BuildContext context) { return Consumer<AccountProvider>( builder: (context, provider, child) { return PopupMenuButton<UserAccount>( icon: CircleAvatar( backgroundImage: NetworkImage(provider.currentAccount?.avatarUrl ?? 'https://picsum.photos/200'), ), itemBuilder: (context) { return [ // 添加新账户选项 const PopupMenuItem( value: null, child: Row( children: [ Icon(Icons.add), SizedBox(width: 8), Text("添加账户"), ], ), ), const PopupMenuDivider(), // 已保存账户列表 ...provider.accounts.map((account) { return PopupMenuItem<UserAccount>( value: account, child: Row( children: [ CircleAvatar( backgroundImage: NetworkImage(account.avatarUrl), ), const SizedBox(width: 8), Text(account.username), if (account.id == provider.currentAccount?.id) const Padding( padding: EdgeInsets.only(left: 8), child: Icon(Icons.check, color: Colors.blue), ), ], ), ); }), const PopupMenuDivider(), // 删除账户选项 PopupMenuItem( onTap: () => _showDeleteDialog(context, provider), child: const Row( children: [ Icon(Icons.delete, color: Colors.red), SizedBox(width: 8), Text("删除账户", style: TextStyle(color: Colors.red)), ], ), ), ]; }, onSelected: (account) { if (account != null) { provider.switchAccount(account.id); } else { Navigator.push(context, MaterialPageRoute(builder: (_) => const AddAccountPage())); } }, ); }, ); } void _showDeleteDialog(BuildContext context, AccountProvider provider) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text("删除账户"), content: const Text("确定要删除当前账户吗?"), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text("取消"), ), TextButton( onPressed: () { if (provider.currentAccount != null) { provider.removeAccount(provider.currentAccount!.id); } Navigator.pop(context); }, child: const Text("删除", style: TextStyle(color: Colors.red)), ), ], ), ); } }
添加账户页面(示例登录页)
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class AddAccountPage extends StatefulWidget { const AddAccountPage({super.key}); @override State<AddAccountPage> createState() => _AddAccountPageState(); } class _AddAccountPageState extends State<AddAccountPage> { final _usernameController = TextEditingController(); Future<void> _login() async { // 模拟登录接口请求,替换为真实逻辑 final newAccount = UserAccount( id: DateTime.now().millisecondsSinceEpoch.toString(), username: _usernameController.text, avatarUrl: "https://picsum.photos/200/200?random=${_usernameController.text}", authToken: "token_${_usernameController.text}", ); await Provider.of<AccountProvider>(context, listen: false).addAccount(newAccount); Navigator.pop(context); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("登录新账户")), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _usernameController, decoration: const InputDecoration(labelText: "用户名"), ), TextField( decoration: const InputDecoration(labelText: "密码"), obscureText: true, ), const SizedBox(height: 24), ElevatedButton( onPressed: _login, child: const Text("登录"), ), ], ), ), ); } }
5. 全局初始化配置
在main函数中初始化Provider和账户数据:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( ChangeNotifierProvider( create: (context) => AccountProvider()..init(), child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Consumer<AccountProvider>( builder: (context, provider, child) { return Text(provider.currentAccount?.username ?? "未登录"); }, ), actions: const [AccountSwitcher()], ), body: Center( child: Consumer<AccountProvider>( builder: (context, provider, child) { return Text( provider.currentAccount != null ? "当前账户:${provider.currentAccount!.username}" : "请登录账户", style: const TextStyle(fontSize: 18), ); }, ), ), ), ); } }
内容的提问来源于stack exchange,提问作者Mohammed Hussein
相关产品推荐
相关产品推荐

