diff --git a/star_lock/images/icon_lock_manipulation_circle.png b/star_lock/images/icon_lock_manipulation_circle.png new file mode 100644 index 00000000..c07d665f Binary files /dev/null and b/star_lock/images/icon_lock_manipulation_circle.png differ diff --git a/star_lock/images/icon_puzzlepiece_extension.png b/star_lock/images/icon_puzzlepiece_extension.png new file mode 100644 index 00000000..4c825ebf Binary files /dev/null and b/star_lock/images/icon_puzzlepiece_extension.png differ diff --git a/star_lock/images/icon_slider_horizontal.png b/star_lock/images/icon_slider_horizontal.png new file mode 100644 index 00000000..af9e03ae Binary files /dev/null and b/star_lock/images/icon_slider_horizontal.png differ diff --git a/star_lock/images/lan/lan_en.json b/star_lock/images/lan/lan_en.json index 5329363d..33927d3a 100644 --- a/star_lock/images/lan/lan_en.json +++ b/star_lock/images/lan/lan_en.json @@ -801,5 +801,6 @@ "《隐私政策》": "《Privacy Policy》", "的全部内容。点击“同意”即表示您同意并接受全部条款。若选择不同意,将无法使用我们的产品和服务,并会退出应用。": "The entire content of. By clicking \"Agree\", you agree and accept all terms. If you choose not to agree, you will not be able to use our products and services and will exit the application.", "不同意":"Disagree", - "同意":"agree" + "同意":"agree", + "常用程序":"Common Programs" } diff --git a/star_lock/images/lan/lan_keys.json b/star_lock/images/lan/lan_keys.json index 1e99b098..7e0ecda3 100644 --- a/star_lock/images/lan/lan_keys.json +++ b/star_lock/images/lan/lan_keys.json @@ -800,5 +800,6 @@ "《隐私政策》": "《隐私政策》", "的全部内容。点击“同意”即表示您同意并接受全部条款。若选择不同意,将无法使用我们的产品和服务,并会退出应用。": "的全部内容。点击“同意”即表示您同意并接受全部条款。若选择不同意,将无法使用我们的产品和服务,并会退出应用。", "不同意":"不同意", - "同意":"同意" + "同意":"同意", + "常用程序":"常用程序" } diff --git a/star_lock/images/lan/lan_zh.json b/star_lock/images/lan/lan_zh.json index dca1f068..c49440be 100644 --- a/star_lock/images/lan/lan_zh.json +++ b/star_lock/images/lan/lan_zh.json @@ -803,6 +803,8 @@ "《隐私政策》": "《隐私政策》", "的全部内容。点击“同意”即表示您同意并接受全部条款。若选择不同意,将无法使用我们的产品和服务,并会退出应用。": "的全部内容。点击“同意”即表示您同意并接受全部条款。若选择不同意,将无法使用我们的产品和服务,并会退出应用。", "不同意":"不同意", - "同意":"同意" + "同意":"同意", + "常用程序":"常用程序" + } diff --git a/star_lock/images/xhj_main_bg.jpg b/star_lock/images/xhj_main_bg.jpg new file mode 100644 index 00000000..db17324c Binary files /dev/null and b/star_lock/images/xhj_main_bg.jpg differ diff --git a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_list_page.dart b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_list_page.dart new file mode 100644 index 00000000..c1941350 --- /dev/null +++ b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_list_page.dart @@ -0,0 +1,65 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:star_lock/flavors.dart'; +import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_state.dart'; +import 'package:star_lock/tools/showTipView.dart'; +import 'package:star_lock/tools/titleAppBar.dart'; +import 'package:star_lock/widget/flavors_img.dart'; + +import '../../../appRouters.dart'; +import '../../../app_settings/app_colors.dart'; +import '../../../blue/blue_manage.dart'; +import '../../../blue/io_tool/io_tool.dart'; +import '../../../common/XSConstantMacro/XSConstantMacro.dart'; +import '../../../tools/appRouteObserver.dart'; +import '../../../tools/commonDataManage.dart'; +import '../../../tools/dateTool.dart'; +import '../../../tools/eventBusEventManage.dart'; +import '../../../tools/storage.dart'; +import '../../../translations/trans_lib.dart'; +import '../../lockMian/entity/lockListInfo_entity.dart'; +import 'lockDetail_logic.dart'; + +class LockDetailListPage extends StatefulWidget { + final String title; + final List items; + + const LockDetailListPage({Key? key, required this.title, required this.items}) + : super(key: key); + + @override + State createState() => _LockDetailListPageState(); +} + +class _LockDetailListPageState extends State + with TickerProviderStateMixin, RouteAware { + late LockDetailLogic logic; + late LockDetailState state; + + @override + void initState() { + super.initState(); + logic = Get.find(); + state = Get.find().state; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: TitleAppBar( + barTitle: widget.title, + haveBack: true, + iconColor: AppColors.blackColor, + titleColor: AppColors.blackColor, + ), + body: ListView.builder( + padding: EdgeInsets.only(top: 15.h), + itemCount: widget.items.length, + itemBuilder: (context, index) => widget.items[index]), + ); + } +} diff --git a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_main_page.dart b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_main_page.dart index b830bb89..eb1d8703 100644 --- a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_main_page.dart +++ b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_main_page.dart @@ -18,7 +18,6 @@ class LockDetailMainPage extends StatefulWidget { class _LockDetailMainPageState extends State { @override void initState() { - // TODO: implement initState super.initState(); } @@ -26,26 +25,37 @@ class _LockDetailMainPageState extends State { Widget build(BuildContext context) { final bool isOnlyOneData; LockListInfoItemEntity keyInfos = LockListInfoItemEntity(); - // LockMainEntity lockEntity = LockMainEntity(); - dynamic obj = ModalRoute.of(context)?.settings.arguments; - // if (obj != null && (obj["lockMainEntity"] != null)) { - // lockEntity = obj["lockMainEntity"]; - // } if (obj != null && (obj["keyInfo"] != null)) { keyInfos = obj["keyInfo"]; } isOnlyOneData = obj["isOnlyOneData"]; - return Scaffold( - backgroundColor: Colors.white, - appBar: TitleAppBar( - barTitle: F.navTitle, - haveBack: true, - backgroundColor: AppColors.mainColor), - body: LockDetailPage( - isOnlyOneData: isOnlyOneData, lockListInfoItemEntity: keyInfos), - // body: Container(), - ); + return F.sw( + defaultCall: () => Scaffold( + backgroundColor: Colors.white, + appBar: TitleAppBar( + barTitle: F.navTitle, + haveBack: true, + backgroundColor: AppColors.mainColor), + body: LockDetailPage( + isOnlyOneData: isOnlyOneData, + lockListInfoItemEntity: keyInfos), + // body: Container(), + ), + xhjCall: () => Scaffold( + backgroundColor: Colors.white, + appBar: TitleAppBar( + barTitle: keyInfos.lockAlias, + haveBack: true, + backgroundColor: Colors.white, + titleColor: AppColors.blackColor, + iconColor: AppColors.blackColor, + ), + body: LockDetailPage( + isOnlyOneData: isOnlyOneData, + lockListInfoItemEntity: keyInfos), + // body: Container(), + )); } } diff --git a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart index 0e1d0613..9164f2f5 100644 --- a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart +++ b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:star_lock/flavors.dart'; +import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_list_page.dart'; import 'package:star_lock/tools/showTipView.dart'; import 'package:star_lock/widget/flavors_img.dart'; @@ -84,12 +85,6 @@ class _LockDetailPageState extends State state.lockUserNo = state.keyInfos.value.lockUserNo!; - // if (state.lockUserNo == 0) { - // state.bottomBtnisEable.value = false; - // } else { - // state.bottomBtnisEable.value = true; - // } - if (state.keyInfos.value.keyStatus == XSConstantMacro.keyStatusWaitIneffective || state.keyInfos.value.keyStatus == XSConstantMacro.keyStatusFrozen || @@ -145,43 +140,242 @@ class _LockDetailPageState extends State Widget xhjWidget() { return Scaffold( backgroundColor: Colors.white, - body: SingleChildScrollView( - child: Obx(() { - return Column( - children: [ - SizedBox( - height: 45.h, + body: Obx(() { + return Column( + children: [ + SizedBox( + height: 35.h, + ), + xhjInfoView(), + SizedBox( + height: 35.h, + ), + labelText('images/icon_slider_horizontal.png', '功能'.tr, () { + Get.to( + LockDetailListPage(title: '功能'.tr, items: getBottomWidget())); + }), + labelText('images/icon_puzzlepiece_extension.png', '配件'.tr, () { + Get.to(LockDetailListPage( + title: '配件'.tr, items: getAttachmentWidget())); + }), + ], + ); + }), + ); + } + + Widget labelText(String img, String text, var onTap) { + return GestureDetector( + onTap: onTap, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 0.05.sw, vertical: 15.h), + padding: EdgeInsets.symmetric(horizontal: 0.05.sw, vertical: 20.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + offset: Offset(0, 0), + blurRadius: 10.r, + spreadRadius: 0, ), - infoView(), - SizedBox( - height: 35.h, - ), - labelGridView('功能'.tr, getBottomWidget()), - labelListView('配件'.tr, getAttachmentWidget()), - SizedBox( - height: 45.h, - ), - Padding( - padding: EdgeInsets.only(left: 52.w, right: 52.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - buttonView('关锁'.tr, () { - if (state.openDoorBtnisUneable.value == true) { - startUnLock(); - } - }), - buttonView('开锁'.tr, () { - if (state.openDoorBtnisUneable.value == true) { + ]), + child: Row( + children: [ + Image.asset( + img, + width: 32.r, + height: 32.r, + ), + SizedBox( + width: 15.w, + ), + Text( + text, + style: TextStyle(fontSize: 24.sp, color: AppColors.blackColor), + ), + const Spacer(), + Icon( + Icons.arrow_forward_ios_rounded, + size: 18.r, + ) + ], + ), + ), + ); + } + + Widget xhjInfoView() { + return Container( + width: 0.9.sw, + height: 0.6.sw, + decoration: BoxDecoration( + image: const DecorationImage( + image: AssetImage('images/xhj_main_bg.jpg'), + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.circular(20.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + offset: const Offset(0, 0), + blurRadius: 10.r, + spreadRadius: 0, + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Spacer(), + Obx(() { + return GestureDetector( + onTap: state.openDoorBtnisUneable.value == true + ? () { + setState(() { startOpenLock(); - } - }), + }); + } + : null, + onLongPressStart: state.openDoorBtnisUneable.value == true + ? (details) { + setState(() { + startUnLock(); + }); + } + : null, + child: Container( + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.9), + borderRadius: BorderRadius.circular(100.w), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + offset: Offset(0, 0), + blurRadius: 10.r, + spreadRadius: 0, + ), + ]), + margin: EdgeInsets.only(left: 35.w, bottom: 15.h), + child: Stack( + children: [ + FlavorsImg( + child: Image.asset( + state.openDoorBtnisUneable.value == false + ? 'images/main/icon_main_openLockBtn_grey.png' + : (state.isOpenPassageMode.value == 1 + ? 'images/main/icon_main_normallyOpenMode_center.png' + : 'images/main/icon_main_openLockBtn_center.png'), + width: 96.r, + height: 96.r, + // color: AppColors.primaryTopColor, + ), + ), + state.openDoorBtnisUneable.value == false + ? Positioned( + child: FlavorsImg( + child: Image.asset( + 'images/main/icon_main_openLockBtn_grey.png', + width: 96.r, + height: 96.r, + ), + ), + ) + : state.openLockBtnState.value == 1 + ? buildRotationTransition( + width: 96.r, + height: 96.r, + ) + : Positioned( + child: FlavorsImg( + child: Image.asset( + state.isOpenPassageMode.value == 1 + ? 'images/main/icon_main_normallyOpenMode_circle.png' + : 'images/main/icon_main_openLockBtn_circle.png', + width: 96.r, + height: 96.r, + ), + )), ], ), ), - ], - ); - }), + ); + }), + Padding( + padding: EdgeInsets.only(left: 15.w, right: 00.w, bottom: 15.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + TranslationLoader + .lanKeys!.clickUnlockAndHoldDownClose!.tr, + style: TextStyle( + color: AppColors.darkGrayTextColor, + fontSize: 20.sp, + ), + ), + adminInfoView(center: false, max: false), + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Image.asset( + showElectricIcon(state.electricQuantity.value), + width: 30.w, + height: 24.w), + SizedBox(width: 2.w), + Text("${state.electricQuantity.value}%", + style: TextStyle( + fontSize: 18.sp, + color: AppColors.darkGrayTextColor)), + SizedBox(width: 2.w), + Icon( + Icons.info, // 使用内置的 warning 图标,它是一个叹号 + color: AppColors.mainColor, // 设置图标颜色为红色 + size: 25.w, // 设置图标大小为 30 + ), + SizedBox(width: 20.w), + ], + ), + SizedBox( + height: 15.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Image.asset( + showElectricIcon(state.electricQuantity.value), + width: 30.w, + height: 24.w), + SizedBox(width: 2.w), + Text("--%", + style: TextStyle( + fontSize: 18.sp, + color: AppColors.darkGrayTextColor)), + SizedBox(width: 2.w), + Icon( + Icons.info, // 使用内置的 warning 图标,它是一个叹号 + color: AppColors.mainColor, // 设置图标颜色为红色 + size: 25.w, // 设置图标大小为 30 + ), + SizedBox(width: 20.w), + ], + ) + ], + ) + ], + ), + ) + ], ), ); } @@ -271,212 +465,6 @@ class _LockDetailPageState extends State ); } - //操作锁按钮 - Widget buttonView(String text, VoidCallback? onPressed) { - bool openLockBtnState = state.openLockBtnState.value != 0; - return ElevatedButton( - onPressed: openLockBtnState ? null : onPressed, - style: ElevatedButton.styleFrom( - backgroundColor: openLockBtnState - ? AppColors.mainBackgroundColor - : AppColors.mainColor, - shape: RoundedRectangleBorder( - // 设置按钮的形状 - borderRadius: BorderRadius.circular(8), // 圆角的大小 - )), - child: SizedBox( - height: 0.12.sh, - width: 0.12.sh, - child: Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - text, - style: TextStyle( - color: - openLockBtnState ? AppColors.mainColor : Colors.white), - ), - if (openLockBtnState) - Container( - margin: EdgeInsets.only(left: 15.w), - width: 15.r, - height: 15.r, - child: CircularProgressIndicator( - strokeWidth: 3.5, - color: AppColors.mainColor, - ), - ) - ], - ), - ), - ), - ); - } - - Widget infoView() { - return Padding( - padding: EdgeInsets.only(left: 35.w, right: 10.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - state.lockAlias.value, - style: TextStyle( - fontSize: 28.sp, - fontWeight: FontWeight.w400, - color: state.isOpenPassageMode.value == 1 - ? AppColors.openPassageModeColor - : AppColors.darkGrayTextColor), - ), - SizedBox( - height: 15.h, - ), - adminInfoView(center: false, max: false), - ], - ), - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - FlavorsImg( - child: Image.asset( - showElectricIcon(state.electricQuantity.value), - width: 30.w, - height: 24.w), - ), - SizedBox(width: 2.w), - Text("${state.electricQuantity.value}%", - style: TextStyle( - fontSize: 18.sp, color: AppColors.darkGrayTextColor)), - SizedBox(width: 2.w), - FlavorsImg( - child: Icon( - Icons.info, // 使用内置的 warning 图标,它是一个叹号 - color: AppColors.mainColor, // 设置图标颜色为红色 - size: 25.w, // 设置图标大小为 30 - ), - ), - SizedBox(width: 20.w), - ], - ), - SizedBox( - height: 15.h, - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - FlavorsImg( - child: Image.asset( - showElectricIcon(state.electricQuantity.value), - width: 30.w, - height: 24.w), - ), - SizedBox(width: 2.w), - Text("--%", - style: TextStyle( - fontSize: 18.sp, color: AppColors.darkGrayTextColor)), - SizedBox(width: 2.w), - FlavorsImg( - child: Icon( - Icons.info, // 使用内置的 warning 图标,它是一个叹号 - color: AppColors.mainColor, // 设置图标颜色为红色 - size: 25.w, // 设置图标大小为 30 - ), - ), - SizedBox(width: 20.w), - ], - ) - ], - ) - ], - ), - ); - } - - //带标签的 GridView 布局 - Widget labelGridView(String text, List children) { - return Column( - children: [ - Align( - alignment: Alignment.topLeft, - child: Padding( - padding: EdgeInsets.only( - left: 40.w, - top: 20.h, - bottom: 20.h, - ), - child: Text(text))), - SizedBox( - height: 200.h, - child: Row( - children: [ - Expanded( - child: GridView.count( - padding: EdgeInsets.only(left: 15.w, right: 15.w), - crossAxisCount: 2, - childAspectRatio: 0.7, - crossAxisSpacing: 25.h, - mainAxisSpacing: 25.w, - scrollDirection: Axis.horizontal, - children: children), - ), - Padding( - padding: EdgeInsets.only(left: 0.w, right: 4.w), - child: const Icon(Icons.arrow_forward_ios_rounded), - ), - ], - ), - ), - ], - ); - } - - //带标签的 ListView 布局 - Widget labelListView(String text, List children) { - return Column( - children: [ - Align( - alignment: Alignment.topLeft, - child: Padding( - padding: EdgeInsets.only( - left: 40.w, - top: 20.h, - bottom: 20.h, - ), - child: Text(text))), - SizedBox( - height: 100.h, - child: Row( - children: [ - Expanded( - child: GridView.count( - padding: EdgeInsets.only(left: 15.w, right: 15.w), - crossAxisCount: 1, - childAspectRatio: 0.7, - crossAxisSpacing: 25.h, - mainAxisSpacing: 25.w, - scrollDirection: Axis.horizontal, - children: children), - ), - Padding( - padding: EdgeInsets.only(left: 0.w, right: 4.w), - child: Icon(Icons.arrow_forward_ios_rounded), - ), - ], - ), - ), - ], - ); - } - Widget topWidget() { return Column( children: [ @@ -510,7 +498,6 @@ class _LockDetailPageState extends State children: [ GestureDetector( onTap: () { - // logic.getStarLockStatus(); ShowTipView().showSureAlertDialog( "${"锁电量更新时间:".tr}${DateTool().dateToYMDHNString(state.keyInfos.value.electricQuantityDate!.toString())}"); }, @@ -581,7 +568,8 @@ class _LockDetailPageState extends State children: [ Center( child: GestureDetector( - onTap: state.openDoorBtnisUneable.value == true ? () { + onTap: state.openDoorBtnisUneable.value == true + ? () { setState(() { startOpenLock(); }); @@ -619,7 +607,10 @@ class _LockDetailPageState extends State ), ) : state.openLockBtnState.value == 1 - ? buildRotationTransition() + ? buildRotationTransition( + width: 330.w, + height: 330.w, + ) : Positioned( child: FlavorsImg( child: Image.asset( @@ -690,7 +681,7 @@ class _LockDetailPageState extends State : TranslationLoader.lanKeys!.normalUser!.tr), style: TextStyle(fontSize: 20.sp, color: AppColors.darkGrayTextColor), ), - if (add) SizedBox(width: 20.w) else SizedBox(width: 80.w), + if (add) SizedBox(width: 20.w) else SizedBox(width: 40.w), FlavorsImg( child: Image.asset( state.keyInfos.value.remoteEnable == 1 @@ -742,7 +733,8 @@ class _LockDetailPageState extends State } //旋转动画 - Widget buildRotationTransition() { + Widget buildRotationTransition( + {required double width, required double height}) { return Positioned( child: RotationTransition( //设置动画的旋转中心 @@ -755,8 +747,8 @@ class _LockDetailPageState extends State state.isOpenPassageMode.value == 1 ? 'images/main/icon_main_normallyOpenMode_circle.png' : 'images/main/icon_main_openLockBtn_circle.png', - width: 330.w, - height: 330.w, + width: width, + height: height, ), ), ), @@ -1080,9 +1072,81 @@ class _LockDetailPageState extends State return showWidgetArr; } - // Widget bottomItem(String iconUrl, String name, bool openDoorBtnisUneable, bool bottomBtnisEable, Function() onClick) { + Widget child = F.sw( + defaultCall: () => Container( + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 42.w, + height: 42.h, + child: FlavorsImg( + black: true, + child: Image.asset(iconUrl, + width: 42.w, + height: 42.w, + color: openDoorBtnisUneable + ? (bottomBtnisEable + ? AppColors.mainColor + : AppColors.lockDetailBottomBtnUneable) + : AppColors.lockDetailBottomBtnUneable, + fit: BoxFit.fitWidth), + ), + ), + SizedBox(height: 10.w), + Expanded( + child: Text(name, + style: TextStyle( + fontSize: 20.sp, + color: openDoorBtnisUneable + ? (bottomBtnisEable + ? AppColors.blackColor + : AppColors.lockDetailBottomBtnUneable) + : AppColors.lockDetailBottomBtnUneable), + textAlign: TextAlign.center)) + ], + ), + ), + xhjCall: () => Container( + color: Colors.white, + margin: EdgeInsets.symmetric(vertical: 5.h), + padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.w), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 42.w, + height: 42.h, + child: FlavorsImg( + black: true, + child: Image.asset(iconUrl, + width: 42.w, + height: 42.w, + color: openDoorBtnisUneable + ? (bottomBtnisEable + ? AppColors.mainColor + : AppColors.lockDetailBottomBtnUneable) + : AppColors.lockDetailBottomBtnUneable, + fit: BoxFit.fitWidth), + ), + ), + SizedBox(width: 20.w), + Text( + name, + style: TextStyle( + fontSize: 20.sp, + color: openDoorBtnisUneable + ? (bottomBtnisEable + ? AppColors.blackColor + : AppColors.lockDetailBottomBtnUneable) + : AppColors.lockDetailBottomBtnUneable), + ), + ], + ), + )); return GestureDetector( onTap: openDoorBtnisUneable ? (bottomBtnisEable @@ -1091,41 +1155,7 @@ class _LockDetailPageState extends State logic.showToast("请在锁旁边完成第一次开锁".tr); }) : null, - child: Container( - // height: 300.h, - color: Colors.white, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - width: 42.w, - height: 42.h, - child: FlavorsImg( - black: true, - child: Image.asset(iconUrl, - width: 42.w, - height: 42.w, - color: openDoorBtnisUneable - ? (bottomBtnisEable - ? AppColors.mainColor - : AppColors.lockDetailBottomBtnUneable) - : AppColors.lockDetailBottomBtnUneable, - fit: BoxFit.fitWidth), - ), - ), - SizedBox(height: 10.w), - Expanded( - child: Text(name, - style: TextStyle( - fontSize: 20.sp, - color: openDoorBtnisUneable - ? (bottomBtnisEable - ? AppColors.blackColor - : AppColors.lockDetailBottomBtnUneable) - : AppColors.lockDetailBottomBtnUneable), - textAlign: TextAlign.center)) - ], - )), + child: child, ); } diff --git a/star_lock/lib/main/lockMian/lockList/lockListGroup_page.dart b/star_lock/lib/main/lockMian/lockList/lockListGroup_view.dart similarity index 72% rename from star_lock/lib/main/lockMian/lockList/lockListGroup_page.dart rename to star_lock/lib/main/lockMian/lockList/lockListGroup_view.dart index 4ae2731d..40ee97e3 100644 --- a/star_lock/lib/main/lockMian/lockList/lockListGroup_page.dart +++ b/star_lock/lib/main/lockMian/lockList/lockListGroup_view.dart @@ -1,31 +1,34 @@ - import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../../app_settings/app_colors.dart'; import '../entity/lockListInfo_entity.dart'; -class LockListGroupPage extends StatefulWidget { - const LockListGroupPage({Key? key, - this.child, - this.onTap, - required this.groupItem, - required this.typeImgList}) +class LockListGroupView extends StatefulWidget { + const LockListGroupView( + {Key? key, + this.child, + this.onTap, + this.backgroundColor, + this.textStyle, + required this.groupItem, + required this.typeImgList}) : super(key: key); final Widget? child; final List typeImgList; final Function()? onTap; final GroupList groupItem; + final Color? backgroundColor; + final TextStyle? textStyle; @override - State createState() => _LockListGroupPageState(); + State createState() => _LockListGroupViewState(); } -class _LockListGroupPageState extends State { +class _LockListGroupViewState extends State { bool _isExpanded = true; final Duration _animationDuration = const Duration(milliseconds: 200); - bool _isCheck = false; @override Widget build(BuildContext context) { @@ -33,7 +36,7 @@ class _LockListGroupPageState extends State { mainAxisSize: MainAxisSize.min, children: [ Container( - color: Colors.white, + color: widget.backgroundColor ?? Colors.white, height: 80.h, child: Row( children: _buildExpandRowList(), @@ -55,18 +58,19 @@ class _LockListGroupPageState extends State { widgetList.add(GestureDetector( child: Container( width: ScreenUtil().screenWidth, - color: Colors.white, + color: widget.backgroundColor ?? Colors.white, child: Row( children: [ SizedBox(width: 40.w), Text( widget.groupItem.groupName ?? '', - style: TextStyle(color: AppColors.blackColor, fontSize: 22.sp), + style: widget.textStyle ?? + TextStyle(color: AppColors.blackColor, fontSize: 22.sp), ), Expanded( child: SizedBox( - width: 10.w, - )), + width: 10.w, + )), AnimatedRotation( turns: _isExpanded ? -0.5 : 0, duration: _animationDuration, diff --git a/star_lock/lib/main/lockMian/lockList/lockList_page.dart b/star_lock/lib/main/lockMian/lockList/lockList_page.dart index 66f07890..38416753 100644 --- a/star_lock/lib/main/lockMian/lockList/lockList_page.dart +++ b/star_lock/lib/main/lockMian/lockList/lockList_page.dart @@ -10,7 +10,7 @@ import '../../../common/XSConstantMacro/XSConstantMacro.dart'; import '../../../tools/appRouteObserver.dart'; import '../../../tools/dateTool.dart'; import '../entity/lockListInfo_entity.dart'; -import 'lockListGroup_page.dart'; +import 'lockListGroup_view.dart'; import 'lockList_logic.dart'; class LockListPage extends StatefulWidget { @@ -58,7 +58,7 @@ class _LockListPageState extends State with RouteAware { //设备多层级列表 Widget _buildLockExpandedList(context, index, GroupList itemData) { List lockItemList = itemData.lockList ?? []; - return LockListGroupPage( + return LockListGroupView( onTap: () { //是否选中组 if (itemData.isChecked) { diff --git a/star_lock/lib/main/lockMian/lockList/lockList_xhj_page.dart b/star_lock/lib/main/lockMian/lockList/lockList_xhj_page.dart index b283e009..7f05d2e1 100644 --- a/star_lock/lib/main/lockMian/lockList/lockList_xhj_page.dart +++ b/star_lock/lib/main/lockMian/lockList/lockList_xhj_page.dart @@ -10,7 +10,7 @@ import '../../../common/XSConstantMacro/XSConstantMacro.dart'; import '../../../tools/appRouteObserver.dart'; import '../../../tools/dateTool.dart'; import '../entity/lockListInfo_entity.dart'; -import 'lockListGroup_page.dart'; +import 'lockListGroup_view.dart'; import 'lockList_logic.dart'; class LockListXHJPage extends StatefulWidget { @@ -36,259 +36,136 @@ class _LockListXHJPageState extends State with RouteAware { } groupDataList.addAll( widget.lockListInfoGroupEntity.groupList as Iterable); - - return Scaffold( - floatingActionButton: FloatingActionButton( - shape: const CircleBorder(), - onPressed: () { - Get.toNamed(Routers.selectLockTypePage); - }, - backgroundColor: AppColors.mainColor, - child: const Icon( - Icons.add, - color: AppColors.darkGrayTextColor, - ), + return SafeArea( + bottom: false, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.topRight, + child: IconButton( + onPressed: () { + Get.toNamed(Routers.selectLockTypePage); + }, + icon: Icon( + Icons.add_circle, + size: 48.w, + color: AppColors.mainColor, + )), + ), + Padding( + padding: EdgeInsets.only(left: 45.w), + child: Text( + '常用程序'.tr, + style: TextStyle( + fontSize: 32.sp, + color: AppColors.blackColor, + fontWeight: FontWeight.w600, + ), + ), + ), + Expanded( + child: ListView.separated( + itemCount: groupDataList.length, + itemBuilder: (context, index) { + GroupList itemData = groupDataList[index]; + return _buildLockExpandedList(context, index, itemData); + }, + shrinkWrap: true, + physics: const AlwaysScrollableScrollPhysics(), + separatorBuilder: (context, index) { + return const Divider( + height: 1, + color: AppColors.greyLineColor, + ); + }), + ), + ], ), - body: ListView.separated( - itemCount: groupDataList.length, - itemBuilder: (context, index) { - GroupList itemData = groupDataList[index]; - return _buildLockExpandedList(context, index, itemData); - }, - shrinkWrap: true, - physics: const AlwaysScrollableScrollPhysics(), - separatorBuilder: (context, index) { - return const Divider( - height: 1, - color: AppColors.greyLineColor, - ); - }), ); } //设备多层级列表 Widget _buildLockExpandedList(context, index, GroupList itemData) { List lockItemList = itemData.lockList ?? []; - return LockListGroupPage( - onTap: () { - //是否选中组 - if (itemData.isChecked) { - } else {} - setState(() {}); - }, - typeImgList: const [], - groupItem: itemData, - child: ListView.separated( - itemCount: lockItemList.length, + return LockListGroupView( + onTap: () { + setState(() {}); + }, + textStyle: TextStyle(fontSize: 28.sp, fontWeight: FontWeight.w600), + backgroundColor: Colors.transparent, + typeImgList: const [], + groupItem: itemData, + child: GridView.extent( + padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.w), + maxCrossAxisExtent: Get.width * .6, + childAspectRatio: 1.4, + crossAxisSpacing: 15.w, + mainAxisSpacing: 15.h, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (context, index) { - return const Divider(height: 1, color: AppColors.greyLineColor); - }, - itemBuilder: (c, index) { - LockListInfoItemEntity keyInfo = lockItemList[index]; - bool isLast = false; - if (lockItemList.length == index + 1) { - isLast = true; - } + children: forItems(lockItemList), + )); + } - return Slidable( - key: ValueKey(keyInfo.keyId), - endActionPane: ActionPane( - extentRatio: 0.2, - motion: const ScrollMotion(), - children: [ - SlidableAction( - onPressed: (BuildContext context) { - state.lockListInfoItemEntity = keyInfo; - logic.deleyLockLogicOfRoles(); - }, - backgroundColor: Colors.red, - foregroundColor: Colors.white, - label: '删除'.tr, - padding: EdgeInsets.only(left: 5.w, right: 5.w), - ), - ], - ), - child: lockInfoListItem(keyInfo, isLast, () { - if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || - keyInfo.keyType == XSConstantMacro.keyTypeLoop) && - (keyInfo.keyStatus == - XSConstantMacro.keyStatusWaitIneffective)) { - logic.showToast("您的钥匙未生效".tr); - return; - } - if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || - keyInfo.keyType == XSConstantMacro.keyTypeLoop) && - (keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen)) { - logic.showToast("您的钥匙已冻结".tr); - return; - } - if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || - keyInfo.keyType == XSConstantMacro.keyTypeLoop) && - (keyInfo.keyStatus == XSConstantMacro.keyStatusExpired)) { - logic.showToast("您的钥匙已过期".tr); - return; - } - Get.toNamed(Routers.lockDetailMainPage, arguments: { - // "lockMainEntity": widget.lockMainEntity, - "keyInfo": keyInfo, - "isOnlyOneData": false, - }); - }), - ); - }), - ); + List forItems(List lockItemList) { + List list = []; + for (int i = 0, j = 0; i < lockItemList.length; i++, j++) { + LockListInfoItemEntity keyInfo = lockItemList[i]; + bool isLast = false; + if (lockItemList.length == i + 1) { + isLast = true; + } + list.add(Slidable( + key: ValueKey(keyInfo.keyId), + endActionPane: ActionPane( + extentRatio: 0.2, + motion: const ScrollMotion(), + children: [ + SlidableAction( + onPressed: (BuildContext context) { + state.lockListInfoItemEntity = keyInfo; + logic.deleyLockLogicOfRoles(); + }, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + label: '删除'.tr, + padding: EdgeInsets.only(left: 5.w, right: 5.w), + ), + ], + ), + child: lockInfoListItem(keyInfo, isLast, () { + if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || + keyInfo.keyType == XSConstantMacro.keyTypeLoop) && + (keyInfo.keyStatus == XSConstantMacro.keyStatusWaitIneffective)) { + logic.showToast("您的钥匙未生效".tr); + return; + } + if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || + keyInfo.keyType == XSConstantMacro.keyTypeLoop) && + (keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen)) { + logic.showToast("您的钥匙已冻结".tr); + return; + } + if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || + keyInfo.keyType == XSConstantMacro.keyTypeLoop) && + (keyInfo.keyStatus == XSConstantMacro.keyStatusExpired)) { + logic.showToast("您的钥匙已过期".tr); + return; + } + Get.toNamed(Routers.lockDetailMainPage, arguments: { + "keyInfo": keyInfo, + "isOnlyOneData": false, + }); + }), + )); + } + return list; } Widget lockInfoListItem( LockListInfoItemEntity keyInfo, bool isLast, Function() action) { - return F.sw( - defaultCall: () => _defaultLockInfoListItem(keyInfo, isLast, action), - xhjCall: () => _xhjLockInfoListItem(keyInfo, isLast, action)); - } - - Widget _defaultLockInfoListItem( - LockListInfoItemEntity keyInfo, bool isLast, Function() action) { - return GestureDetector( - onTap: action, - child: Container( - // height: 122.h, - margin: isLast - ? EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w, bottom: 20.w) - : EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w), - decoration: BoxDecoration( - color: ((keyInfo.keyType == XSConstantMacro.keyTypeTime || - keyInfo.keyType == XSConstantMacro.keyTypeLoop) && - (keyInfo.keyStatus == - XSConstantMacro.keyStatusWaitIneffective || - keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen || - keyInfo.keyStatus == XSConstantMacro.keyStatusExpired)) - ? AppColors.greyBackgroundColor - : Colors.white, - borderRadius: BorderRadius.circular(20.w), - ), - child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - height: 20.h, - ), - Row( - children: [ - SizedBox(width: 30.w), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - keyInfo.lockAlias!, - style: TextStyle( - fontSize: 24.sp, - fontWeight: FontWeight.w500, - color: keyInfo.passageMode == 1 - ? AppColors.openPassageModeColor - : AppColors.darkGrayTextColor), - ), - ], - ), - ), - SizedBox(width: 20.w), - Image.asset( - logic.showElectricIcon(keyInfo.electricQuantity!), - width: 30.w, - height: 24.w, - ), - SizedBox(width: 2.w), - Text( - "${keyInfo.electricQuantity!}%", - style: TextStyle( - fontSize: 18.sp, color: AppColors.darkGrayTextColor), - ), - SizedBox(width: 30.w), - ], - ), - SizedBox(height: 5.h), - Visibility( - visible: keyInfo.passageMode == 1 ? true : false, - child: Row( - children: [ - SizedBox(width: 30.w), - Container( - padding: EdgeInsets.only(right: 5.w, left: 5.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5.w), - color: AppColors.openPassageModeColor, - ), - child: Text("常开模式开启".tr, - style: TextStyle( - fontSize: 18.sp, - color: AppColors.appBarIconColor)), - ), - ], - )), - SizedBox(height: 5.h), - Visibility( - visible: keyInfo.lockSetting!.remoteUnlock == 1 ? true : false, - child: Row( - children: [ - SizedBox(width: 30.w), - Text( - "远程开锁".tr, - style: TextStyle( - fontSize: 18.sp, color: AppColors.darkGrayTextColor), - ), - ], - )), - SizedBox(height: 20.h), - Visibility( - visible: ((keyInfo.keyType == XSConstantMacro.keyTypeTime || - keyInfo.keyType == XSConstantMacro.keyTypeLoop) && - (keyInfo.keyStatus == - XSConstantMacro.keyStatusWaitIneffective || - keyInfo.keyStatus == - XSConstantMacro.keyStatusFrozen || - keyInfo.keyStatus == - XSConstantMacro.keyStatusExpired)) - ? true - : false, - // visible: true, - child: Row( - children: [ - SizedBox(width: 30.w), - Container( - padding: EdgeInsets.only(right: 5.w, left: 5.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5.w), - color: - DateTool().compareTimeIsOvertime(keyInfo.endDate!) - ? AppColors.listTimeYellowColor - : AppColors.mainColor, - ), - child: Text(logic.getKeyEffective(keyInfo), - style: TextStyle(fontSize: 18.sp, color: Colors.white) - // child: Text(logic.compareTimeIsOvertime(keyInfo.endDate!) ? "已过期" : "余${logic.compareTimeGetDaysFromNow(keyInfo.endDate!)}天", style: TextStyle(fontSize: 18.sp, color: Colors.white) - ), - ), - ], - )), - SizedBox(height: 5.h), - Row( - children: [ - SizedBox(width: 30.w), - Text( - "${logic.getUseKeyTypeStr(keyInfo.startDate, keyInfo.endDate, keyInfo.keyType)}/${keyInfo.isLockOwner == 1 ? '超级管理员'.tr : (keyInfo.keyRight == 1 ? "授权管理员".tr : "普通用户".tr)}", - style: TextStyle( - fontSize: 18.sp, color: AppColors.darkGrayTextColor), - ), - ], - ), - SizedBox(height: 20.h), - ], - ), - ), - ); + return _xhjLockInfoListItem(keyInfo, isLast, action); } Widget _xhjLockInfoListItem( @@ -296,10 +173,6 @@ class _LockListXHJPageState extends State with RouteAware { return GestureDetector( onTap: action, child: Container( - // height: 122.h, - margin: isLast - ? EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w, bottom: 20.w) - : EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w), decoration: BoxDecoration( color: ((keyInfo.keyType == XSConstantMacro.keyTypeTime || keyInfo.keyType == XSConstantMacro.keyTypeLoop) && @@ -308,7 +181,7 @@ class _LockListXHJPageState extends State with RouteAware { keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen || keyInfo.keyStatus == XSConstantMacro.keyStatusExpired)) ? AppColors.greyBackgroundColor - : Colors.white, + : Colors.white.withOpacity(0.8), borderRadius: BorderRadius.circular(20.w), ), child: Padding( @@ -324,21 +197,7 @@ class _LockListXHJPageState extends State with RouteAware { height: 32.r, color: AppColors.mainColor, ), - SizedBox( - width: 8.w, - ), - Expanded( - child: Text(keyInfo.lockAlias!, - style: TextStyle( - fontSize: 32.sp, - fontWeight: FontWeight.w500, - color: keyInfo.passageMode == 1 - ? AppColors.openPassageModeColor - : AppColors.darkGrayTextColor, - overflow: TextOverflow.ellipsis, - ), - maxLines: 1), - ), + Spacer(), Text( logic.getUseKeyTypeStr( keyInfo.startDate, keyInfo.endDate, keyInfo.keyType), @@ -347,6 +206,19 @@ class _LockListXHJPageState extends State with RouteAware { ) ], ), + SizedBox( + height: 15.h, + ), + Text(keyInfo.lockAlias!, + style: TextStyle( + fontSize: 32.sp, + fontWeight: FontWeight.w500, + color: keyInfo.passageMode == 1 + ? AppColors.openPassageModeColor + : AppColors.darkGrayTextColor, + overflow: TextOverflow.ellipsis, + ), + maxLines: 1), Visibility( visible: keyInfo.passageMode == 1 ? true : false, child: Padding( @@ -383,7 +255,6 @@ class _LockListXHJPageState extends State with RouteAware { ], ), )), - SizedBox(height: 20.h), Visibility( visible: ((keyInfo.keyType == XSConstantMacro.keyTypeTime || keyInfo.keyType == XSConstantMacro.keyTypeLoop) && @@ -442,11 +313,6 @@ class _LockListXHJPageState extends State with RouteAware { ), ], ), - Text( - keyInfo.hwVersion ?? "", - style: TextStyle( - fontSize: 18.sp, color: AppColors.darkGrayTextColor), - ), ], ), ), diff --git a/star_lock/lib/main/lockMian/lockMain/lockMain_page.dart b/star_lock/lib/main/lockMian/lockMain/lockMain_page.dart index ec382028..4e6fd7e8 100644 --- a/star_lock/lib/main/lockMian/lockMain/lockMain_page.dart +++ b/star_lock/lib/main/lockMian/lockMain/lockMain_page.dart @@ -114,6 +114,17 @@ class _StarLockMainPageState extends State with BaseWidget { body: child, ); } + child = F.sw( + defaultCall: () => child, + xhjCall: () => Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('images/xhj_main_bg.jpg'), + fit: BoxFit.cover, + ), + ), + child: child, + )); return child; } diff --git a/star_lock/lib/main/lockMian/lockMain/xhj/lockMain_xhj_page.dart b/star_lock/lib/main/lockMian/lockMain/xhj/lockMain_xhj_page.dart index 364a7f92..c7106e91 100644 --- a/star_lock/lib/main/lockMian/lockMain/xhj/lockMain_xhj_page.dart +++ b/star_lock/lib/main/lockMian/lockMain/xhj/lockMain_xhj_page.dart @@ -40,43 +40,49 @@ class _StarLockMainXHJPageState extends State init: LockMainXHJLogic(), builder: (LockMainXHJLogic logic) { return Scaffold( - backgroundColor: const Color(0xFFF5F5F5), - body: SafeArea( - bottom: false, - child: Stack( - children: [ - pageView( - widget: StarLockMainPage( - showAppBar: false, - showDrawer: false, - ), - logic: logic, - index: 0, + backgroundColor: Colors.white, + body: Stack( + children: [ + pageView( + widget: StarLockMainPage( + showAppBar: false, + showDrawer: false, ), - pageView( - widget: LockMallPage( + logic: logic, + index: 0, + ), + pageView( + widget: SafeArea( + bottom: false, + child: LockMallPage( showAppBar: false, ), - logic: logic, - index: 1, ), - pageView( - widget: MessageListPage( + logic: logic, + index: 1, + ), + pageView( + widget: SafeArea( + bottom: false, + child: MessageListPage( showAppBar: false, ), - logic: logic, - index: 2, ), - pageView( - widget: MinePersonInfoPage( + logic: logic, + index: 2, + ), + pageView( + widget: SafeArea( + bottom: false, + child: MinePersonInfoPage( showAppBar: false, showAbout: true, ), - logic: logic, - index: 3, ), - ], - ), + logic: logic, + index: 3, + ), + ], ), bottomNavigationBar: Container( padding: EdgeInsets.only(top: 20.h), @@ -93,11 +99,8 @@ class _StarLockMainXHJPageState extends State top: false, child: Row( children: [ - navigationBarItem( - Icons.key, - TranslationLoader.lanKeys!.device!.tr, - logic, - 0, () { + navigationBarItem(Icons.key, + TranslationLoader.lanKeys!.device!.tr, logic, 0, () { logic.setIndex(0); }), navigationBarItem(Icons.shopping_cart, '商城'.tr, logic, 1, diff --git a/star_lock/lib/tools/titleAppBar.dart b/star_lock/lib/tools/titleAppBar.dart index 587a9c4b..0f5b3b8a 100644 --- a/star_lock/lib/tools/titleAppBar.dart +++ b/star_lock/lib/tools/titleAppBar.dart @@ -42,7 +42,7 @@ class _TitleAppBarState extends State { elevation: 0, leading: widget.haveOtherLeftWidget! ? widget.leftWidget - : (widget.haveBack! + : (widget.haveBack ?? false ? IconButton( icon: Icon(Icons.arrow_back_ios, color: widget.iconColor ?? Colors.white), @@ -50,13 +50,12 @@ class _TitleAppBarState extends State { if (widget.backAction != null) { widget.backAction!(); } else { - if(EasyLoading.isShow){ + if (EasyLoading.isShow) { EasyLoading.dismiss(); } Navigator.pop(context); } - } - ) + }) : Container()), backgroundColor: widget.backgroundColor ?? Colors.white, title: widget.haveTitleWidget!