feat: 增加弹出气泡、搜索设备页面
This commit is contained in:
parent
64f1a28997
commit
8501b5ea48
@ -11,6 +11,21 @@
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<!-- 系统通知 -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- 蓝牙相关权限 -->
|
||||
<!-- Android 12以下需要的蓝牙权限 -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
|
||||
<!-- Android 12及以上需要的蓝牙权限 -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
|
||||
<!-- 位置权限(Android 12以下蓝牙扫描需要) -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:name="${applicationName}"
|
||||
|
||||
22
lib/app.dart
22
lib/app.dart
@ -11,7 +11,7 @@ import 'package:starwork_flutter/routes/app_pages.dart';
|
||||
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||
|
||||
class App extends StatefulWidget {
|
||||
App({super.key, required this.initialRoute});
|
||||
App({super.key, required this.initialRoute});
|
||||
|
||||
String initialRoute;
|
||||
|
||||
@ -29,15 +29,17 @@ class _AppState extends State<App> {
|
||||
builder: (_, child) {
|
||||
return GetMaterialApp(
|
||||
theme: ThemeData(
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
brightness: Brightness.light,
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
cursorColor: Colors.blue.shade200,
|
||||
selectionColor: Colors.blue.shade100,
|
||||
selectionHandleColor: Colors.blue.shade300,
|
||||
),
|
||||
dialogBackgroundColor: Colors.white,
|
||||
),
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
brightness: Brightness.light,
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
cursorColor: Colors.blue.shade200,
|
||||
selectionColor: Colors.blue.shade100,
|
||||
selectionHandleColor: Colors.blue.shade300,
|
||||
),
|
||||
dialogBackgroundColor: Colors.white,
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: Colors.white,
|
||||
)),
|
||||
// 必须配置以下三个本地化代理
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate, // 提供Material组件本地化字符串
|
||||
|
||||
@ -46,4 +46,132 @@ class AppPermission {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 请求蓝牙权限(Android 12及以上需要)
|
||||
static Future<bool> requestBluetoothPermissions() async {
|
||||
try {
|
||||
// Android 12及以上需要的蓝牙权限
|
||||
List<Permission> bluetoothPermissions = [
|
||||
Permission.bluetoothScan,
|
||||
Permission.bluetoothConnect,
|
||||
Permission.bluetoothAdvertise,
|
||||
];
|
||||
|
||||
// Android 12以下需要的位置权限
|
||||
List<Permission> locationPermissions = [
|
||||
Permission.location,
|
||||
Permission.locationWhenInUse,
|
||||
];
|
||||
|
||||
bool bluetoothGranted = true;
|
||||
bool locationGranted = true;
|
||||
|
||||
// 检查并请求蓝牙权限(Android 12+)
|
||||
Map<Permission, PermissionStatus> bluetoothStatuses = await bluetoothPermissions.request();
|
||||
for (var status in bluetoothStatuses.values) {
|
||||
if (status != PermissionStatus.granted) {
|
||||
bluetoothGranted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查并请求位置权限(Android 12以下或蓝牙扫描需要)
|
||||
Map<Permission, PermissionStatus> locationStatuses = await locationPermissions.request();
|
||||
for (var status in locationStatuses.values) {
|
||||
if (status != PermissionStatus.granted) {
|
||||
locationGranted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasPermission = bluetoothGranted && locationGranted;
|
||||
|
||||
if (hasPermission) {
|
||||
print("蓝牙权限已授予");
|
||||
} else {
|
||||
print("蓝牙权限被拒绝");
|
||||
}
|
||||
|
||||
return hasPermission;
|
||||
|
||||
} catch (e) {
|
||||
print("请求蓝牙权限失败: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查蓝牙扫描权限
|
||||
static Future<bool> checkBluetoothScanPermission() async {
|
||||
try {
|
||||
var status = await Permission.bluetoothScan.status;
|
||||
return status == PermissionStatus.granted;
|
||||
} catch (e) {
|
||||
// 如果权限不存在(Android 12以下),检查位置权限
|
||||
return await checkLocationPermission();
|
||||
}
|
||||
}
|
||||
|
||||
// 检查蓝牙连接权限
|
||||
static Future<bool> checkBluetoothConnectPermission() async {
|
||||
try {
|
||||
var status = await Permission.bluetoothConnect.status;
|
||||
return status == PermissionStatus.granted;
|
||||
} catch (e) {
|
||||
// 如果权限不存在(Android 12以下),默认返回true
|
||||
print("蓝牙连接权限检查失败,可能是Android 12以下版本: $e");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查蓝牙广播权限
|
||||
static Future<bool> checkBluetoothAdvertisePermission() async {
|
||||
try {
|
||||
var status = await Permission.bluetoothAdvertise.status;
|
||||
return status == PermissionStatus.granted;
|
||||
} catch (e) {
|
||||
// 如果权限不存在(Android 12以下),默认返回true
|
||||
print("蓝牙广播权限检查失败,可能是Android 12以下版本: $e");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查位置权限(Android 12以下蓝牙扫描需要)
|
||||
static Future<bool> checkLocationPermission() async {
|
||||
var status = await Permission.location.status;
|
||||
return status == PermissionStatus.granted;
|
||||
}
|
||||
|
||||
// 请求位置权限
|
||||
static Future<bool> requestLocationPermission() async {
|
||||
try {
|
||||
final PermissionStatus status = await Permission.location.request();
|
||||
|
||||
if (status == PermissionStatus.granted) {
|
||||
print("位置权限已授予");
|
||||
return true;
|
||||
} else if (status == PermissionStatus.denied) {
|
||||
print("位置权限被拒绝");
|
||||
return false;
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
print("位置权限被永久拒绝,跳转到设置页面");
|
||||
openAppSettings();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
print("请求位置权限失败: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查所有蓝牙相关权限
|
||||
static Future<bool> checkAllBluetoothPermissions() async {
|
||||
bool scanPermission = await checkBluetoothScanPermission();
|
||||
bool connectPermission = await checkBluetoothConnectPermission();
|
||||
bool advertisePermission = await checkBluetoothAdvertisePermission();
|
||||
bool locationPermission = await checkLocationPermission();
|
||||
|
||||
return scanPermission && connectPermission && advertisePermission && locationPermission;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/common/widgets/custom_dialog_widget.dart';
|
||||
|
||||
class BaseController extends GetxController {
|
||||
void showToast(String message) {
|
||||
@ -24,6 +25,24 @@ class BaseController extends GetxController {
|
||||
EasyLoading.showError(message.tr);
|
||||
}
|
||||
|
||||
void showCustomDialog({
|
||||
required String title,
|
||||
required Widget content,
|
||||
required VoidCallback onConfirm,
|
||||
String? confirmText
|
||||
}) {
|
||||
Get.dialog(
|
||||
CustomDialogWidget(
|
||||
title: title,
|
||||
content: content,
|
||||
onConfirm: onConfirm,
|
||||
confirmText: confirmText,
|
||||
),
|
||||
barrierDismissible: false, // 点击遮罩是否关闭
|
||||
useSafeArea: true, // 推荐保持默认
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
if (EasyLoading.isShow) {
|
||||
|
||||
110
lib/common/widgets/custom_dialog_widget.dart
Normal file
110
lib/common/widgets/custom_dialog_widget.dart
Normal file
@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class CustomDialogWidget extends StatefulWidget {
|
||||
CustomDialogWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.content,
|
||||
required this.onConfirm,
|
||||
String? confirmText,
|
||||
}) : confirmText = confirmText ?? '确认'.tr;
|
||||
|
||||
final String title;
|
||||
final Widget content;
|
||||
final VoidCallback onConfirm;
|
||||
final String confirmText;
|
||||
|
||||
@override
|
||||
State<CustomDialogWidget> createState() => _CustomDialogWidgetState();
|
||||
}
|
||||
|
||||
class _CustomDialogWidgetState extends State<CustomDialogWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14.r), // 圆角
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 18.h),
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
widget.content,
|
||||
SizedBox(height: 10.h),
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Colors.grey, // 右侧边框颜色
|
||||
width: 0.5, // 右侧边框宽度
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
// 左侧文本占一半
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 42.h,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: Colors.grey, // 右侧边框颜色
|
||||
width: 0.5, // 右侧边框宽度
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'取消'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 14.sp, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
// 右侧文本占一半
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
widget.onConfirm();
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 42.h,
|
||||
child: Text(
|
||||
widget.confirmText,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,7 @@ class F {
|
||||
static String get apiHost {
|
||||
switch (appFlavor) {
|
||||
case Flavor.skyDev:
|
||||
return 'http://10.0.2.2/api';
|
||||
return 'http://192.168.1.136/api';
|
||||
case Flavor.skyPre:
|
||||
return 'https://loacl.work.star-lock.cn/api';
|
||||
case Flavor.skyRelease:
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||
import 'package:starwork_flutter/views/device/searchDevice/search_device_binding.dart';
|
||||
import 'package:starwork_flutter/views/device/searchDevice/search_device_view.dart';
|
||||
import 'package:starwork_flutter/views/home/home_binding.dart';
|
||||
import 'package:starwork_flutter/views/home/home_view.dart';
|
||||
import 'package:starwork_flutter/views/login/forgotPassword/forgot_password_binding.dart';
|
||||
@ -60,5 +62,10 @@ class AppPages {
|
||||
page: () => const SetNewPasswordView(),
|
||||
binding: SetNewPasswordBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: AppRoutes.searchDevice,
|
||||
page: () => const SearchDeviceView(),
|
||||
binding: SearchDeviceBinding(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -8,4 +8,5 @@ class AppRoutes{
|
||||
static const String inputVerificationCode = '/inputVerificationCode';
|
||||
static const String forgotPassword = '/forgotPassword';
|
||||
static const String setNewPassword = '/setNewPassword';
|
||||
static const String searchDevice = '/searchDevice';
|
||||
}
|
||||
9
lib/views/device/searchDevice/search_device_binding.dart
Normal file
9
lib/views/device/searchDevice/search_device_binding.dart
Normal file
@ -0,0 +1,9 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/views/device/searchDevice/search_device_controller.dart';
|
||||
|
||||
class SearchDeviceBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<SearchDeviceController>(() => SearchDeviceController());
|
||||
}
|
||||
}
|
||||
22
lib/views/device/searchDevice/search_device_controller.dart
Normal file
22
lib/views/device/searchDevice/search_device_controller.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:starwork_flutter/base/app_permission.dart';
|
||||
import 'package:starwork_flutter/base/base_controller.dart';
|
||||
|
||||
class SearchDeviceController extends BaseController {
|
||||
// 搜索状态管理
|
||||
final RxBool _isSearching = false.obs;
|
||||
|
||||
// Getter
|
||||
bool get isSearching => _isSearching.value;
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
}
|
||||
}
|
||||
42
lib/views/device/searchDevice/search_device_view.dart
Normal file
42
lib/views/device/searchDevice/search_device_view.dart
Normal file
@ -0,0 +1,42 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/views/device/searchDevice/search_device_controller.dart';
|
||||
import 'package:starwork_flutter/views/device/searchDevice/widget/search_device_rotating_icon_widget.dart';
|
||||
|
||||
class SearchDeviceView extends GetView<SearchDeviceController> {
|
||||
const SearchDeviceView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
children: [
|
||||
Obx(() => Text(
|
||||
controller.isSearching ? '搜索设备中'.tr : '搜索设备'.tr,
|
||||
)),
|
||||
SizedBox(
|
||||
width: 8.w,
|
||||
),
|
||||
Obx(
|
||||
() => SearchDeviceRotatingIconWidget(
|
||||
isRotating: controller.isSearching,
|
||||
radius: 10.w,
|
||||
rotationDuration: 1500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
// TODO: 添加设备搜索结果列表
|
||||
child: Center(
|
||||
child: Text('设备搜索页面'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
class SearchDeviceRotatingIconWidget extends StatefulWidget {
|
||||
/// 是否正在旋转
|
||||
final bool isRotating;
|
||||
/// 旋转速度(毫秒)
|
||||
final int rotationDuration;
|
||||
/// 图标颜色
|
||||
final Color? color;
|
||||
/// 图标半径
|
||||
final double? radius;
|
||||
|
||||
const SearchDeviceRotatingIconWidget({
|
||||
super.key,
|
||||
required this.isRotating,
|
||||
this.rotationDuration = 1000,
|
||||
this.color = Colors.grey,
|
||||
this.radius,
|
||||
});
|
||||
|
||||
@override
|
||||
_SearchDeviceRotatingIconWidgetState createState() => _SearchDeviceRotatingIconWidgetState();
|
||||
}
|
||||
|
||||
class _SearchDeviceRotatingIconWidgetState extends State<SearchDeviceRotatingIconWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
duration: Duration(milliseconds: widget.rotationDuration),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
// 根据初始状态决定是否开始动画
|
||||
if (widget.isRotating) {
|
||||
_animationController.repeat();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(SearchDeviceRotatingIconWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
// 当isRotating状态发生变化时,控制动画的开始和停止
|
||||
if (widget.isRotating != oldWidget.isRotating) {
|
||||
if (widget.isRotating) {
|
||||
_startRotation();
|
||||
} else {
|
||||
_stopRotation();
|
||||
}
|
||||
}
|
||||
|
||||
// 当旋转速度发生变化时,更新动画时长
|
||||
if (widget.rotationDuration != oldWidget.rotationDuration) {
|
||||
_animationController.duration = Duration(milliseconds: widget.rotationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// 开始旋转动画
|
||||
void _startRotation() {
|
||||
if (!_animationController.isAnimating) {
|
||||
_animationController.repeat();
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止旋转动画
|
||||
void _stopRotation() {
|
||||
if (_animationController.isAnimating) {
|
||||
_animationController.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _animationController,
|
||||
builder: (context, child) {
|
||||
return Transform.rotate(
|
||||
angle: _animationController.value * 2 * 3.14159,
|
||||
child: CupertinoActivityIndicator(
|
||||
radius: widget.radius ?? 10.w,
|
||||
color: widget.color,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/base/app_permission.dart';
|
||||
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||
import 'package:super_tooltip/super_tooltip.dart';
|
||||
import 'package:starwork_flutter/views/home/widget/home_attendance_chart_area_widget.dart';
|
||||
import 'package:starwork_flutter/views/home/widget/home_carousel_area_widget.dart';
|
||||
@ -175,10 +176,15 @@ class HomeView extends GetView<HomeController> {
|
||||
SizedBox(
|
||||
width: 14.w,
|
||||
),
|
||||
Icon(
|
||||
Icons.cancel,
|
||||
color: const Color(0xFFEE9846),
|
||||
size: 18.sp,
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
controller.isOpenNotificationPermission.value = true;
|
||||
},
|
||||
child: Icon(
|
||||
Icons.cancel,
|
||||
color: const Color(0xFFEE9846),
|
||||
size: 18.sp,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -263,12 +269,12 @@ class HomeView extends GetView<HomeController> {
|
||||
{
|
||||
'title': '搜索设备',
|
||||
'icon': Icons.sensors_rounded,
|
||||
'onTap': () => _handleMenuTap('添加设备'),
|
||||
'onTap': () => _handleMenuTap(0),
|
||||
},
|
||||
{
|
||||
'title': '加入团队',
|
||||
'icon': Icons.group_add,
|
||||
'onTap': () => _handleMenuTap('创建群组'),
|
||||
'onTap': () => _handleMenuTap(1),
|
||||
},
|
||||
];
|
||||
|
||||
@ -329,7 +335,9 @@ class HomeView extends GetView<HomeController> {
|
||||
size: 18.sp,
|
||||
color: Colors.black87,
|
||||
),
|
||||
SizedBox(width: 8.w,),
|
||||
SizedBox(
|
||||
width: 8.w,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
title,
|
||||
@ -348,12 +356,13 @@ class HomeView extends GetView<HomeController> {
|
||||
}
|
||||
|
||||
/// 处理菜单点击事件
|
||||
void _handleMenuTap(String menuTitle) {
|
||||
Get.snackbar(
|
||||
'提示',
|
||||
'点击了:$menuTitle',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
void _handleMenuTap(int menuIndex) {
|
||||
switch (menuIndex) {
|
||||
case 0:
|
||||
Get.toNamed(AppRoutes.searchDevice);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,127 +72,44 @@ class LoginController extends BaseController {
|
||||
required String content,
|
||||
required VoidCallback onConfirm,
|
||||
}) {
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14.r), // 圆角
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
showCustomDialog(
|
||||
title: title,
|
||||
content: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 22.h),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
TextSpan(
|
||||
text: '请你阅读并同意'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(22.w),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '请你阅读并同意'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '《用户协议》'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '和'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '《隐私政策》'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextSpan(
|
||||
text: '《用户协议》'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.h),
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Colors.grey, // 右侧边框颜色
|
||||
width: 0.5, // 右侧边框宽度
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '和'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.grey,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
// 左侧文本占一半
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 42.h,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: Colors.grey, // 右侧边框颜色
|
||||
width: 0.5, // 右侧边框宽度
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'取消'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 14.sp, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
// 右侧文本占一半
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
onConfirm();
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 42.h,
|
||||
child: Text(
|
||||
'同意并继续'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextSpan(
|
||||
text: '《隐私政策》'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.blue,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onConfirm: onConfirm,
|
||||
confirmText: '同意并继续'.tr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/base/app_permission.dart';
|
||||
@ -201,10 +202,16 @@ class MessagesView extends GetView<MessagesController> {
|
||||
SizedBox(
|
||||
width: 14.w,
|
||||
),
|
||||
Icon(
|
||||
Icons.cancel,
|
||||
color: const Color(0xFFEE9846),
|
||||
size: 18.sp,
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
controller.homeController.isOpenNotificationPermission.value =
|
||||
true;
|
||||
},
|
||||
child: Icon(
|
||||
Icons.cancel,
|
||||
color: const Color(0xFFEE9846),
|
||||
size: 18.sp,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -217,25 +224,31 @@ class MessagesView extends GetView<MessagesController> {
|
||||
return RefreshIndicator(
|
||||
onRefresh: _onRefresh,
|
||||
// 基础样式配置
|
||||
color: const Color(0xFF4A90E2), // 刷新指示器颜色
|
||||
backgroundColor: Colors.white, // 背景颜色
|
||||
|
||||
color: const Color(0xFF4A90E2),
|
||||
// 刷新指示器颜色
|
||||
backgroundColor: Colors.white,
|
||||
// 背景颜色
|
||||
|
||||
// 控制下拉触发距离的关键属性
|
||||
displacement: 60.0, // 刷新指示器距离顶部的距离(默认40.0)
|
||||
edgeOffset: 0.0, // 边缘偏移量(默认0.0)
|
||||
|
||||
displacement: 60.0,
|
||||
// 刷新指示器距离顶部的距离(默认40.0)
|
||||
edgeOffset: 0.0,
|
||||
// 边缘偏移量(默认0.0)
|
||||
|
||||
// 控制下拉幅度的关键属性
|
||||
triggerMode: RefreshIndicatorTriggerMode.onEdge, // 触发模式
|
||||
triggerMode: RefreshIndicatorTriggerMode.onEdge,
|
||||
// 触发模式
|
||||
// RefreshIndicatorTriggerMode.onEdge: 在边缘触发(默认)
|
||||
// RefreshIndicatorTriggerMode.anywhere: 在任何位置都可以触发
|
||||
|
||||
|
||||
// 描边宽度
|
||||
strokeWidth: 2.5, // 刷新指示器的描边宽度(默认2.0)
|
||||
|
||||
strokeWidth: 2.5,
|
||||
// 刷新指示器的描边宽度(默认2.0)
|
||||
|
||||
// 语义标签(用于无障碍功能)
|
||||
semanticsLabel: '下拉刷新消息列表',
|
||||
semanticsValue: '刷新中...',
|
||||
|
||||
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(
|
||||
// 控制滚动物理特性
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user