Flutter 效率:账号切换组件 NAccountSheet
一、需求来源
开发的一个项目目前有四种角色,每种角色进入的界面和各模块的权限都完全不同;测试阶段修改bug,每天都需要切换几十次,不胜其烦,随封装组件 NAccountSheet 提高效率。
核心思路:每次登录成功之后通过控制器调用添加当前账号密码,存储到本地,再次点击弹窗就是最新的账号列表;
效果如下:
二、使用示例
...
buildAccountSheet(),
...
// 账号切换
final accountSheetController = NAccountSheetController();
Widget buildAccountSheet() {
return NAccountSheet(
controller: accountSheetController,
onChanged: (e) {
accountController.text = e.key;
pwdController.text = e.value;
},
);
}
void onClear() {
accountSheetController.clear();
}
三、组件源码
//
// NAccountSheetNewNew.dart
// yl_health_app_v2.20.4.1
//
// Created by shang on 2024/3/27 16:24.
// Copyright © 2024/3/27 shang. All rights reserved.
//
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_templet_project/cache/cache_service.dart';
import 'package:flutter_templet_project/extension/widget_ext.dart';
/// 账号选择器
class NAccountSheet extends StatefulWidget {
const NAccountSheet({
super.key,
this.controller,
this.items = const [],
required this.onChanged,
this.titleCb,
this.subtitleCb,
});
/// 控制器
final NAccountSheetController? controller;
/// 预置数据列表(默认值空)
final List<MapEntry<String, dynamic>> items;
/// 改变回调
final ValueChanged<MapEntry<String, dynamic>> onChanged;
/// 子项标题显示
final String Function(MapEntry<String, dynamic> e)? titleCb;
/// 子项目副标题显示
final String Function(MapEntry<String, dynamic> e)? subtitleCb;
@override
State<NAccountSheet> createState() => _NAccountSheetState();
}
class _NAccountSheetState extends State<NAccountSheet> {
late List<MapEntry<String, dynamic>> items = widget.items;
late MapEntry<String, dynamic>? current = items.isEmpty ? null : items.first;
String get btnTitle => current == null ? "请选择账号" : current!.key;
@override
void dispose() {
widget.controller?._detach(this);
super.dispose();
}
@override
void initState() {
super.initState();
widget.controller?._attach(this);
var map = CacheService().getMap(CACHE_ACCOUNT_List) ?? <String, dynamic>{};
if (map.isNotEmpty) {
updateItems(map.entries.toList());
}
}
@override
Widget build(BuildContext context) {
if (kReleaseMode) {
return const SizedBox();
}
if (items.isEmpty) {
return const SizedBox();
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.red,
padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: onChooseAccount,
child: Text(btnTitle),
),
],
),
);
}
void onChooseAccount() {
showAlertSheet(
message: Text(btnTitle),
actions: items.map((e) {
final title = widget.titleCb?.call(e) ?? e.key;
return ListTile(
dense: true,
onTap: () {
Navigator.of(context).pop();
current = e;
widget.onChanged(e);
setState(() {});
},
title: Text(title),
subtitle: Text(widget.subtitleCb?.call(e) ?? ""),
trailing: Icon(
Icons.check,
color: current?.key == e.key ? Colors.blue : Colors.transparent,
),
);
}).toList(),
);
}
void showAlertSheet({
Widget title = const Text("请选择"),
Widget? message,
required List<Widget> actions,
}) {
CupertinoActionSheet(
title: title,
message: message,
actions: actions,
cancelButton: CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () {
Navigator.pop(context);
},
child: const Text('取消'),
),
).toShowCupertinoModalPopup(context: context);
}
void updateItems(List<MapEntry<String, dynamic>> value) {
value.sort((a, b) => a.key.compareTo(b.key));
items = value;
}
void updateCurrent(MapEntry<String, dynamic>? e) {
current = e;
debugPrint("current: ${current}");
}
}
class NAccountSheetController {
_NAccountSheetState? _anchor;
void _attach(_NAccountSheetState anchor) {
_anchor = anchor;
}
void _detach(_NAccountSheetState anchor) {
if (_anchor == anchor) {
_anchor = null;
}
}
void onChooseAccount() {
assert(_anchor != null);
_anchor!.onChooseAccount();
}
void updateItems(List<MapEntry<String, dynamic>> items) {
assert(_anchor != null);
_anchor!.updateItems(items.reversed.toList());
}
/// 添加账户
void addAccount({
required String account,
required String pwd,
}) {
assert(_anchor != null);
var map = CacheService().getMap(CACHE_ACCOUNT_List) ?? <String, dynamic>{};
map.putIfAbsent(account, () => pwd);
_anchor?.items.forEach((e) {
map.putIfAbsent(e.key, () => e.value);
});
CacheService().setMap(CACHE_ACCOUNT_List, map);
updateItems(map.entries.toList());
_anchor?.updateCurrent(MapEntry(account, pwd));
}
void clear() {
CacheService().remove(CACHE_ACCOUNT_List);
updateItems([]);
_anchor?.updateCurrent(null);
}
}
总结
1、核心是基于极简封装的原则通过将每对账号密码转为 MapEntry,添加到字典存储到本地,实时更新,极其简单;
2、此组件是效率组件,为工作提效随手开发;如果你的app只有一种账号类型,请湖绿;
转载自:https://juejin.cn/post/7369903157646999604