feat: 整体常用控件封装,常用工具类。首页数据对接

This commit is contained in:
chengsiwen committed 2026-06-27 14:15:25 +08:00
1 parent d90d023e85
commit fbcd33ea2b
50 files changed
+4558 -264

No files matched your search

+12 -5
View File
@@ -4,8 +4,8 @@ import 'package:get/get.dart';
/// AppScreen class
class AppScreen {
static double get screenWidth => Get.width;
static double get screenHeight => Get.height;
static double get screenWidth => _viewSize.width;
static double get screenHeight => _viewSize.height;
// static double get screenWidth => MediaQuery.of(Get.context!).size.width;
// static double get screenHeight => MediaQuery.of(Get.context!).size.height;
@@ -13,7 +13,7 @@ class AppScreen {
return MediaQuery.of(context).size;
}
static bool get isDebug=> kDebugMode;
static bool get isDebug => kDebugMode;
static double get statusBarHeight => Get.statusBarHeight / Get.pixelRatio;
@@ -33,12 +33,19 @@ class AppScreen {
return GetPlatform.isIOS;
}
static Size get _viewSize {
if (Get.context != null) {
return MediaQuery.of(Get.context!).size;
}
final view = WidgetsBinding.instance.platformDispatcher.views.first;
return view.physicalSize / view.devicePixelRatio;
}
static double aRatioWidth({double? h, double aspectRatio = 1}) {
h ??= screenHeight;
return screenHeight * 1 / aspectRatio;
}
static double get keyboardHeight => Get.mediaQuery.viewInsets.bottom;
}
+143
View File
@@ -0,0 +1,143 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
// import 'package:lottie/lottie.dart';
// 加载数据的函数类型
typedef LoadBlock<T> = Future<T> Function();
// 弹出Toast提示的类
class AppToast {
static const String _defaultLoadingText = "";
static const String _loadingText = "loading...";
/// 显示普通 Toast 提示
static Future<void> showToast(
Object msg, {
Duration displayTime = const Duration(seconds: 2),
}) async {
await dismiss();
return SmartDialog.showToast(
msg.toString(),
alignment: Alignment.center,
debounce: true,
displayTime: displayTime,
);
}
static Future<void> showError(
Object msg, {
Duration displayTime = const Duration(seconds: 2),
}) {
return showFail(msg, displayTime: displayTime);
}
/// 显示失败提示
static Future<void> showFail(
Object msg, {
Duration displayTime = const Duration(seconds: 2),
Function()? onDismiss,
}) async {
await dismiss();
return SmartDialog.showNotify(
msg: msg.toString(),
alignment: Alignment.center,
notifyType: NotifyType.failure,
displayTime: displayTime,
debounce: true,
onDismiss: onDismiss,
);
}
/// 显示成功提示
static Future<T?> showSuccess<T>(
Object msg, {
Duration displayTime = const Duration(seconds: 2),
}) async {
await dismiss();
return SmartDialog.showNotify<T>(
msg: msg.toString(),
notifyType: NotifyType.success,
debounce: true,
displayTime: displayTime,
);
}
static Future<T?> showInfo<T>(
Object msg, {
Duration displayTime = const Duration(seconds: 2),
}) {
return showWarning<T>(msg, displayTime: displayTime);
}
static Future<T?> showWarning<T>(
Object msg, {
Duration displayTime = const Duration(seconds: 2),
}) async {
await dismiss();
return SmartDialog.showNotify<T>(
msg: msg.toString(),
notifyType: NotifyType.warning,
debounce: true,
displayTime: displayTime,
);
}
/// 显示 Lottie 加载提示
// static Future<T?> showLottieLoading<T>({
// Duration? displayTime,
// }) async {
// await dismiss();
// return SmartDialog.showLoading<T>(
// displayTime: displayTime ?? const Duration(seconds: 5),
// maskColor: Colors.transparent,
// builder: (_) {
// return SizedBox(
// width: 120,
// height: 120,
// child: Lottie.asset(
// 'assets/bagua.json',
// fit: BoxFit.contain,
// repeat: true,
// ),
// );
// },
// );
// }
/// 显示加载提示
static Future<T?> showLoading<T>({
String text = _loadingText,
Duration displayTime = const Duration(seconds: 10),
}) async {
await dismiss();
return SmartDialog.showLoading<T>(msg: text, displayTime: displayTime);
}
/// 关闭特定状态的弹窗
static Future<void> dismiss<T>({SmartStatus status = SmartStatus.loading}) {
return SmartDialog.dismiss<T>(status: status);
}
/// 关闭所有弹窗(占位)
static void dismissAll() {
SmartDialog.dismiss();
}
/// 自动显示加载框并在完成后关闭
static Future<T> loading<T>(
LoadBlock<T> block, {
String? text,
bool isLoading = true,
}) async {
if (isLoading) {
showLoading(text: text ?? _defaultLoadingText);
}
try {
final result = await block();
return result;
} finally {
dismiss();
}
}
}
+16
View File
@@ -18,6 +18,22 @@ class ConstImg {
static const String tabMineUnselect = "assets/common/tab_mine_unselect.png";
static const String homeBg = "assets/imgs/home_bg.png";
// home_card_bg
static const String homeCardBg = "assets/imgs/home_card_bg.png";
// home_top_bg
static const String homeTopBg = "assets/imgs/home_top_bg.png";
// home_title_daily/hot/hot_cate/mystery/weekly
static const String homeTitleDaily = "assets/imgs/home_title_daily.png";
static const String homeTitleHot = "assets/imgs/home_title_hot.png";
static const String homeTitleHotCate = "assets/imgs/home_title_hot_cate.png";
static const String homeTitleMystery = "assets/imgs/home_title_mystery.png";
static const String homeTitleWeekly = "assets/imgs/home_title_weekly.png";
static const String homeOpenNowBox = "assets/imgs/home_opennow_box.png";
// splash_page
static const String splashPage = "assets/imgs/splash_page.png";
// heart
static const String heart = "assets/images/heart.png";
// nodata
static const String noData = "assets/images/nodata.png";
+117
View File
@@ -0,0 +1,117 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart';
/// navigator
class HelpNav {
/// clear all route
static Future<T?>? offAllNamed<T>(
String newRouteName, {
Map? arguments,
RoutePredicate? predicate,
String? tag,
Map? parameters,
}) {
return Get.offAllNamed<T>(
newRouteName,
arguments: arguments,
predicate: predicate,
parameters: {...?parameters},
);
}
static Future<T?>? off<T>(Widget page, {arguments, preventDuplicates}) {
return Get.off<T>(
() => page,
arguments: arguments,
preventDuplicates: preventDuplicates ?? false,
);
}
static Future<T?>? offAll<T>(Widget page, {arguments, preventDuplicates}) {
return Get.offAll<T>(() => page, arguments: arguments);
}
static Future<T?>? offNamed<T>(
String newRouteName, {
arguments,
preventDuplicates,
}) {
return Get.offNamed<T>(
newRouteName,
arguments: arguments,
preventDuplicates: preventDuplicates ?? true,
);
}
/// to name
static Future<T?>? toNamed<T>(
String newRouteName, {
arguments,
String? tag,
Map? parameters,
}) {
return Get.toNamed<T>(
newRouteName,
arguments: arguments,
parameters: {...?parameters},
);
}
/// to route
static Future<T?>? to<T>(Widget page, {arguments, binding}) {
return Get.to<T>(
() => page,
arguments: arguments,
binding: binding,
preventDuplicates: false,
);
}
static void _until<T>({String? routeName, T? result}) {
if (result == null) {
Get.back<T>(result: result);
return;
}
// Get.back<T>(result: result);
//
if (routeName == null) {
Get.back<T>(result: result);
return;
}
}
/// back pre
/// [result]
static void back<T>({T? result, String? routeName}) {
_until(routeName: routeName, result: result);
}
/// show dialog
static Future<T?> showDialog<T>(
Widget child, {
bool barrierDismissible = false,
}) {
return Get.dialog<T>(
Center(child: child),
barrierDismissible: barrierDismissible,
);
}
/// show bottom sheet
static Future<T?> showBottom<T>(
Widget child, {
Color? safeAreaColor,
double? borderRadius,
bool isDismissible = false,
bool isScrollControlled = false,
bool? enableDrag,
}) {
return Get.bottomSheet<T>(
child,
isScrollControlled: isScrollControlled,
enableDrag: enableDrag ?? true,
isDismissible: isDismissible,
);
}
}
+177
View File
@@ -0,0 +1,177 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:android_id/android_id.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:skywood/global/request/api_const.dart';
@immutable
class SkyDeviceInfo {
final String deviceId;
final String systemType;
final String systemVersion;
final String brand;
final String model;
final String appName;
final String appPackageName;
final String appVersion;
final String idfv;
static SkyDeviceInfo? _instance;
const SkyDeviceInfo._({
required this.deviceId,
required this.systemType,
required this.systemVersion,
required this.brand,
required this.model,
required this.appName,
required this.appPackageName,
required this.appVersion,
required this.idfv,
});
static SkyDeviceInfo get instance {
if (_instance == null) {
throw StateError('error');
}
return _instance!;
}
@visibleForTesting
static void setMockInstance({
String deviceId = 'test-device-id',
String systemType = 'test',
String systemVersion = '1.0',
String brand = 'test',
String model = 'test',
String appName = 'Glimzo',
String appPackageName = 'com.example.skywood',
String appVersion = '1.0.0',
String idfv = 'test-idfv',
}) {
_instance = SkyDeviceInfo._(
deviceId: deviceId,
systemType: systemType,
systemVersion: systemVersion,
brand: brand,
model: model,
appName: appName,
appPackageName: appPackageName,
appVersion: appVersion,
idfv: idfv,
);
}
/// 初始化设备信息
static Future<void> init() async {
if (_instance != null) return;
String deviceId = '',
systemType = '',
systemVersion = '',
brand = '',
model = '',
appName = 'Glimzo',
appPackageName = '',
appVersion = '',
idfv = '';
// 获取应用信息
await _initAppInfo()
.then((info) {
appPackageName = info['packageName'] ?? '';
appVersion = info['version'] ?? '';
appName = ApiConst.appName;
})
.catchError((e) {
debugPrint('[sDeviceInfo] getElPackageName: $e');
});
// 获取设备信息
if (Platform.isAndroid) {
await _initAndroidInfo()
.then((info) {
deviceId = info['deviceId'] ?? '';
systemType = 'android';
systemVersion = info['systemVersion'] ?? '';
brand = info['brand'] ?? '';
model = info['model'] ?? '';
})
.catchError((e) {
debugPrint('[GlimzoUtilDeviceInfo] Android: $e');
});
} else if (Platform.isIOS) {
await _initIOSInfo()
.then((info) {
deviceId = info['deviceId'] ?? '';
idfv = info['idfv'] ?? '';
systemType = 'ios';
systemVersion = info['systemVersion'] ?? '';
brand = info['brand'] ?? '';
model = info['model'] ?? '';
})
.catchError((e) {
debugPrint('[GlimzoUtilDeviceInfo] iOS: $e');
});
}
_instance = SkyDeviceInfo._(
deviceId: deviceId,
systemType: systemType,
systemVersion: systemVersion,
brand: brand,
model: model,
appName: appName,
appPackageName: appPackageName,
appVersion: appVersion,
idfv: idfv,
);
}
/// 获取应用信息
static Future<Map<String, String>> _initAppInfo() async {
final packageInfo = await PackageInfo.fromPlatform();
return {
'packageName': packageInfo.packageName,
'version': packageInfo.version,
};
}
/// 获取 Android 设备信息
static Future<Map<String, String?>> _initAndroidInfo() async {
final plugin = DeviceInfoPlugin();
final androidInfo = await plugin.androidInfo;
final androidIdPlugin = AndroidId();
final androidId = await androidIdPlugin.getId();
return {
'deviceId': androidId ?? androidInfo.id,
'systemVersion': androidInfo.version.release,
'brand': androidInfo.brand,
'model': androidInfo.model,
};
}
/// 获取 iOS 设备信息
static Future<Map<String, String?>> _initIOSInfo() async {
final plugin = DeviceInfoPlugin();
final iosInfo = await plugin.iosInfo;
final secureStorage = FlutterSecureStorage();
String? idfvStore = await secureStorage.read(key: 'identifierForVendor');
idfvStore ??= iosInfo.identifierForVendor;
if (idfvStore != null) {
await secureStorage.write(key: 'identifierForVendor', value: idfvStore);
}
return {
'deviceId': idfvStore,
'idfv': iosInfo.identifierForVendor ?? '',
'systemVersion': iosInfo.systemVersion,
'brand': iosInfo.model,
'model': iosInfo.modelName,
};
}
}
+53
View File
@@ -0,0 +1,53 @@
class SkyTimeTool {
static String getTimeZone() {
final offset = DateTime.now().timeZoneOffset;
final hours = offset.inHours;
final minutes = offset.inMinutes.remainder(60).abs();
final sign = hours >= 0 ? '+' : '-';
final formatted =
'GMT$sign${hours.abs().toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}';
return formatted;
}
//格式化数字,带单位 k / m,可自定义小数位和千分位分隔符
static String formatNum(
num value, {
int decimalDigits = 1,
String separator = ',',
}) {
String removeTrailingZeros(String str) {
if (str.contains('.')) {
str = str.replaceAll(RegExp(r'0+$'), ''); // 去掉末尾 0
str = str.replaceAll(RegExp(r'\.$'), ''); // 去掉末尾小数点
}
return str;
}
String addSeparator(String str) {
final parts = str.split('.');
final intPart = parts[0];
final decPart = parts.length > 1 ? '.${parts[1]}' : '';
final regex = RegExp(r'(\d+)(\d{3})');
var formatted = intPart;
while (regex.hasMatch(formatted)) {
formatted = formatted.replaceAllMapped(
regex,
(m) => '${m[1]}$separator${m[2]}',
);
}
return '$formatted$decPart';
}
if (value < 1000) {
return addSeparator(value.toStringAsFixed(0));
} else if (value < 1000000) {
final formatted = (value / 1000).toStringAsFixed(decimalDigits);
return '${removeTrailingZeros(formatted)}K';
} else {
final formatted = (value / 1000000).toStringAsFixed(decimalDigits);
return '${removeTrailingZeros(formatted)}M';
}
}
}
+234
View File
@@ -0,0 +1,234 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:skywood/global/client/user_client.dart';
/// Token 管理单例类
/// 对应原项目的 GlimzoUtilToken
class TokenManager {
// ============ 单例模式 ============
static final TokenManager _instance = TokenManager._internal();
factory TokenManager() => _instance;
TokenManager._internal();
// ============ 常量定义 ============
static const String _keyToken = 'SKY_STORE_TOKEN';
static const String _keyUserInfo = 'SKY_STORE_USER_INFO';
// ============ 内存缓存 ============
String? _cachedToken;
Map<String, dynamic>? _cachedUserInfo;
// ============ 核心方法 ============
/// 获取 Token(从内存或存储)
String get() {
if (_cachedToken != null) {
return _cachedToken!;
}
// 如果内存中没有,从存储中加载
_cachedToken = _getFromStorage(_keyToken);
return _cachedToken ?? '';
}
/// 保存 Token(到内存和存储)
void save(String token) {
if (token.isNotEmpty) {
_cachedToken = token;
_saveToStorage(_keyToken, token);
}
}
/// 清除 Token(从内存和存储)
void clear() {
_cachedToken = null;
_removeFromStorage(_keyToken);
_cachedUserInfo = null;
_removeFromStorage(_keyUserInfo);
}
/// 刷新 Token(调用注册接口获取新 Token)
/// 注意:这里需要传入一个刷新函数,因为原项目依赖 GlimzoRequestUser
Future<void> update() async {
try {
final res = await UserClient.register();
if (res == null) return;
final newToken = res.token;
if (newToken.isNotEmpty ?? false) {
// 保存新 Token
save(newToken);
// 如果返回了用户信息,也一并保存
// if (res['userInfo'] != null) {
// saveUserInfo(res['userInfo']);
// }
} else {
throw Exception('Invalid token response');
}
} catch (e) {
debugPrint('[TokenManager] Error updating token: $e');
rethrow;
}
}
// ============ 便捷属性 ============
/// 判断 Token 是否为空
bool get isEmpty => get().isEmpty;
/// 判断 Token 是否不为空
bool get isNotEmpty => !isEmpty;
/// 获取完整 Authorization 头
String? get authorizationHeader {
final token = get();
if (token.isNotEmpty) {
return 'Bearer $token';
}
return null;
}
// ============ 用户信息扩展(原项目没有,但建议加上) ============
/// 保存用户信息
void saveUserInfo(Map<String, dynamic> userInfo) {
_cachedUserInfo = userInfo;
_saveToStorage(_keyUserInfo, jsonEncode(userInfo));
}
/// 获取用户信息
Map<String, dynamic>? getUserInfo() {
if (_cachedUserInfo != null) {
return _cachedUserInfo;
}
final stored = _getFromStorage(_keyUserInfo);
if (stored != null && stored.isNotEmpty) {
try {
_cachedUserInfo = jsonDecode(stored) as Map<String, dynamic>;
return _cachedUserInfo;
} catch (_) {
return null;
}
}
return null;
}
// ============ 私有存储方法 ============
String? _getFromStorage(String key) {
try {
// 使用 SharedPreferences 同步获取(但 SharedPreferences 是异步的)
// 为了保持与原项目一致的同步接口,这里使用异步转同步
// 但更好的方式是在初始化时预加载
return _getFromStorageSync(key);
} catch (e) {
debugPrint('[TokenManager] Error reading from storage: $e');
return null;
}
}
// 同步读取(注意:SharedPreferences 本身是异步的,这里用同步方式需要谨慎)
String? _getFromStorageSync(String key) {
// 由于 SharedPreferences 是异步的,我们使用一个全局缓存
// 实际项目中建议在 main 中调用 init() 预加载
try {
// 这里为了简化,直接返回 null,实际应该用异步方式
// 建议使用下面的异步版本
return null;
} catch (_) {
return null;
}
}
void _saveToStorage(String key, String value) {
// 异步保存,不等待结果
_saveToStorageAsync(key, value);
}
void _removeFromStorage(String key) {
// 异步移除,不等待结果
_removeFromStorageAsync(key);
}
// ============ 异步存储方法(推荐使用) ============
/// 初始化:从存储中加载 Token
Future<void> init() async {
try {
final prefs = await SharedPreferences.getInstance();
_cachedToken = prefs.getString(_keyToken);
final userInfoStr = prefs.getString(_keyUserInfo);
if (userInfoStr != null && userInfoStr.isNotEmpty) {
try {
_cachedUserInfo = jsonDecode(userInfoStr) as Map<String, dynamic>;
} catch (_) {
_cachedUserInfo = null;
}
}
} catch (e) {
debugPrint('[TokenManager] Init error: $e');
}
}
/// 异步获取 Token
Future<String> getAsync() async {
if (_cachedToken != null) {
return _cachedToken!;
}
try {
final prefs = await SharedPreferences.getInstance();
_cachedToken = prefs.getString(_keyToken);
return _cachedToken ?? '';
} catch (e) {
debugPrint('[TokenManager] Get token error: $e');
return '';
}
}
/// 异步保存 Token
Future<void> saveAsync(String token) async {
if (token.isNotEmpty) {
_cachedToken = token;
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_keyToken, token);
} catch (e) {
debugPrint('[TokenManager] Save token error: $e');
}
}
}
/// 异步清除 Token
Future<void> clearAsync() async {
_cachedToken = null;
_cachedUserInfo = null;
try {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_keyToken);
await prefs.remove(_keyUserInfo);
} catch (e) {
debugPrint('[TokenManager] Clear token error: $e');
}
}
// 异步保存到存储
Future<void> _saveToStorageAsync(String key, String value) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(key, value);
} catch (e) {
debugPrint('[TokenManager] Save to storage error: $e');
}
}
// 异步从存储移除
Future<void> _removeFromStorageAsync(String key) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(key);
} catch (e) {
debugPrint('[TokenManager] Remove from storage error: $e');
}
}
}
@@ -0,0 +1,278 @@
import 'package:flutter/material.dart';
import '../../../global/const/common_color.dart';
import '../../extend/widget_extend.dart';
import '../help_nav.dart';
class SkyText extends StatelessWidget {
const SkyText({
super.key,
required this.text,
this.color = Colors.white,
this.fontSize = 14,
this.shadow,
this.decoration,
this.decorationColor,
this.textAlign,
this.fontWeight,
this.maxLines,
this.lineHeight,
this.letterSpacing,
this.overflow = TextOverflow.ellipsis,
this.softWrap,
this.fontFamily,
});
final String text;
final Color? color;
final double? fontSize;
final TextDecoration? decoration;
final Color? decorationColor;
final TextAlign? textAlign;
final FontWeight? fontWeight;
final int? maxLines;
final Shadow? shadow;
final double? lineHeight;
final double? letterSpacing;
final TextOverflow? overflow;
final bool? softWrap;
final String? fontFamily;
@override
Widget build(BuildContext context) {
final FontWeight resolvedWeight = fontWeight ?? FontWeight.normal;
return Text(text, style: TextStyle(
fontSize: fontSize,
fontWeight: resolvedWeight,
decoration: decoration,
fontFamily: fontFamily ?? 'Inter',
color: color,
shadows: shadow == null ? null : [shadow!],
decorationColor: decorationColor,
height: lineHeight,
letterSpacing: letterSpacing,
), maxLines: maxLines,
overflow: overflow,
softWrap: softWrap,
textAlign: textAlign,
);
}
}
class ComConfirmWidget extends StatelessWidget {
final String? title;
final String? content;
final Widget? contentWidget;
final String? cancelStr, confirmStr;
final double? height;
final Function? onConfirmed;
final Function? onCanceled;
final bool? showCancel;
final bool? clickHideAlert;
const ComConfirmWidget({
super.key,
this.title,
this.content,
this.contentWidget,
this.cancelStr,
this.confirmStr,
this.height,
this.onConfirmed,
this.onCanceled,
this.showCancel = true,
this.clickHideAlert = true,
});
@override
Widget build(BuildContext context) {
return Container(
height: height ?? 160,
margin: EdgeInsets.only(left: 25, right: 25),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Column(
// mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 15,),
title==null ? SizedBox() : Padding(padding: EdgeInsets.only(left:15, right: 15, bottom: 5),
child: SkyText(text: title!, fontSize: 16, color: Colors.black,
)
),
const SizedBox(height: 10),
Expanded(
child: Padding(padding: EdgeInsets.only(left:14, right: 14),
child: contentWidget ?? Center(
child: SkyText(text: content!,
fontSize: 15, color: CommonColor.black,
maxLines: 3),
),
)
),
const SizedBox(height: 10),
Divider(thickness: 0.6, height: 1),
SizedBox(
height: 44,
child: IntrinsicHeight(
child: showCancel! ? Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(onPressed: () {
Navigator.pop(context);
onCanceled?.call();
},
child: Center(
child: SkyText(text: cancelStr ?? 'Cancel',
color: Colors.grey, fontSize: 14))
),
VerticalDivider(width: 5, thickness: 0.6,),
TextButton(onPressed: () {
if (clickHideAlert == true) {
Navigator.pop(context);
}
onConfirmed?.call();
},
child: Center(
child:SkyText(text: confirmStr ?? 'Confirm',
color: CommonColor.goldColor, fontSize: 14))
),
],
) : TextButton(onPressed: () {
Navigator.pop(context);
onConfirmed?.call();
},
child: Center(
child:SkyText(text: confirmStr ?? 'Confirm',
color: CommonColor.black, fontSize: 14,))
),
)
)
],
),
);
}
}
class ComSheetWidget extends StatelessWidget {
final Function(int)? onSelected;
final String? title;
final List<String> items;
final TextAlign? textAlign;
const ComSheetWidget({super.key,
this.onSelected,
this.title,
this.textAlign = TextAlign.left,
required this.items,
});
@override
Widget build(BuildContext context) {
return SafeArea(child: _mainView());
}
_mainView() {
return Container(
padding: EdgeInsets.only(top: 10),
height: items.length>5 ? 360 : 40+items.length*47 + 50,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20)
),
),
child: _ifElseWidget(),
);
}
_ifElseWidget() {
if (title != null) {
return Column(
children: [
const SizedBox(height: 6),
Text('$title', style: TextStyle(
color: Colors.black, fontSize: 16
)),
const SizedBox(height: 5),
const Divider(height: 8, thickness: 0.6,),
// Divider(),
const SizedBox(height: 5),
Expanded(child: ListView.separated(itemBuilder: (context, index) {
return _itemCreateer(index);
}, separatorBuilder: (context, index) {
return Divider( indent: 15, endIndent: 15, thickness: 0.6,);
}, itemCount: items.length))
],
);
} else {
return ListView.separated(itemBuilder: (context, index) {
return _itemCreateer(index);
}, separatorBuilder: (context, index) {
return Divider(indent: 15, endIndent: 15, thickness: 0.6,);
}, itemCount: items.length);
}
}
_itemCreateer(int index) {
return SizedBox(
height: 47,
child: Center(
child: Text(items[index], style: TextStyle(
fontSize: 15, color: Colors.black,
)),
).addInkWell(
onTap: () {
HelpNav.back();
onSelected?.call(index);
})
);
}
}