import 'package:electronic_assistant/base/base_page.dart'; import 'package:electronic_assistant/module/main/controller.dart'; import 'package:electronic_assistant/module/main/drawer/view.dart'; import 'package:electronic_assistant/resource/assets.gen.dart'; import 'package:electronic_assistant/resource/colors.gen.dart'; import 'package:electronic_assistant/resource/string.gen.dart'; import 'package:electronic_assistant/utils/expand.dart'; import 'package:electronic_assistant/utils/toast_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '../../data/consts/constants.dart'; import '../files/view.dart'; import '../home/view.dart'; import 'dart:math' as math; class MainTabPage extends BasePage { MainTabPage({super.key}); final pages = [ const HomePage(), const FilesPage(), ]; @override Widget buildBody(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (bool didPop, dynamic result) async { if (controller.scaffoldKey.currentState?.isDrawerOpen == true) { controller.closeDrawer(); return; } if ((controller.lastPressedAt == null || DateTime.now().difference(controller.lastPressedAt!) > const Duration(seconds: 2))) { controller.setLastPressedAt(DateTime.now()); ToastUtil.showToast(StringName.exitAppTip.tr); } else { controller.exit(); } }, child: Scaffold( extendBody: true, backgroundColor: Colors.transparent, key: controller.scaffoldKey, body: Obx(() { return pages[controller.currentIndex]; }), resizeToAvoidBottomInset: false, floatingActionButton: buildAIChatBtn(), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, bottomNavigationBar: buildBottomAppBar(), drawerEdgeDragWidth: 0, drawer: Drawer( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.zero, ), backgroundColor: "#F6F5F8".toColor(), child: const MainDrawerView(), ), ), ); } @override bool immersive() { return true; } Widget buildAIChatBtn() { return GestureDetector( onTap: () { controller.onChatClick(); }, child: Assets.images.mainTabSecretary .image(key: controller.aiGuideKey, width: 52.w, height: 52.w)); } Widget buildBottomAppBar() { return Container( decoration: BoxDecoration( boxShadow: [ BoxShadow( color: ColorName.black5.withOpacity(0.05), // 阴影颜色 blurRadius: 23, // 阴影模糊半径 spreadRadius: 2, // 阴影扩展半径 offset: const Offset(0, 0), // 阴影位置,向上偏移 ), ], ), child: BottomAppBar( color: Colors.white, height: Constants.bottomBarHeight, padding: EdgeInsets.zero, shape: const CusCircularNotchedRectangle(), child: Flex( mainAxisAlignment: MainAxisAlignment.spaceAround, direction: Axis.horizontal, children: [ Expanded( flex: 1, child: bottomAppBarItem(0), ), SizedBox( width: 62.w, height: double.infinity, child: Align( alignment: const Alignment(0.0, 0.6), child: Text(StringName.mainTabAi.tr, style: TextStyle( fontSize: 10.sp, color: ColorName.tabUnCheckColor))), ), Expanded( flex: 1, child: bottomAppBarItem(1), ), ], ), ), ); } Widget bottomAppBarItem(int index) { return Obx(() { TextStyle style; TabBean tabBean = controller.tabBeans[index]; String imagePath; if (controller.currentIndex == index) { //选中的话 style = TextStyle(fontSize: 10.sp, color: tabBean.txtSelectedColor); imagePath = tabBean.selectedIcon; } else { style = TextStyle(fontSize: 10.sp, color: tabBean.txtNormalColor); imagePath = tabBean.normalIcon; } return GestureDetector( behavior: HitTestBehavior.opaque, child: SizedBox( height: 56.h, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Image.asset(imagePath, width: 24.w, height: 24.w), Text( tabBean.title.tr, style: style, ) ], ), ), ), onTap: () { if (controller.currentIndex != index) { controller.updateIndex(index); } }, ); }); } } class CusCircularNotchedRectangle extends NotchedShape { /// Creates a [CusCircularNotchedRectangle]. /// /// The same object can be used to create multiple shapes. const CusCircularNotchedRectangle(); /// Creates a [Path] that describes a rectangle with a smooth circular notch. /// /// `host` is the bounding box for the returned shape. Conceptually this is /// the rectangle to which the notch will be applied. /// /// `guest` is the bounding box of a circle that the notch accommodates. All /// points in the circle bounded by `guest` will be outside of the returned /// path. /// /// The notch is curve that smoothly connects the host's top edge and /// the guest circle. // TODO(amirh): add an example diagram here. @override Path getOuterPath(Rect host, Rect? guest) { if (guest == null || !host.overlaps(guest)) { return Path()..addRect(host); } // The guest's shape is a circle bounded by the guest rectangle. // So the guest's radius is half the guest width. final double notchRadius = guest.width / 2.0; // We build a path for the notch from 3 segments: // Segment A - a Bezier curve from the host's top edge to segment B. // Segment B - an arc with radius notchRadius. // Segment C - a Bezier curve from segment B back to the host's top edge. // // A detailed explanation and the derivation of the formulas below is // available at: https://goo.gl/Ufzrqn const double s1 = 20.0; const double s2 = 6; final double r = notchRadius; final double a = -1.0 * r - s2; final double b = host.top - guest.center.dy; final double n2 = math.sqrt(b * b * r * r * (a * a + b * b - r * r)); final double p2xA = ((a * r * r) - n2) / (a * a + b * b); final double p2xB = ((a * r * r) + n2) / (a * a + b * b); final double p2yA = math.sqrt(r * r - p2xA * p2xA); final double p2yB = math.sqrt(r * r - p2xB * p2xB); final List p = List.filled(6, null); // p0, p1, and p2 are the control points for segment A. p[0] = Offset(a - s1, b); p[1] = Offset(a, b); final double cmp = b < 0 ? -1.0 : 1.0; p[2] = cmp * p2yA > cmp * p2yB ? Offset(p2xA, p2yA) : Offset(p2xB, p2yB); // p3, p4, and p5 are the control points for segment B, which is a mirror // of segment A around the y axis. p[3] = Offset(-1.0 * p[2]!.dx, p[2]!.dy); p[4] = Offset(-1.0 * p[1]!.dx, p[1]!.dy); p[5] = Offset(-1.0 * p[0]!.dx, p[0]!.dy); // translate all points back to the absolute coordinate system. for (int i = 0; i < p.length; i += 1) { p[i] = p[i]! + guest.center; } return Path() ..moveTo(host.left, host.top) ..lineTo(p[0]!.dx, p[0]!.dy) ..quadraticBezierTo(p[1]!.dx, p[1]!.dy, p[2]!.dx, p[2]!.dy) ..arcToPoint( p[3]!, radius: Radius.circular(notchRadius), clockwise: false, ) ..quadraticBezierTo(p[4]!.dx, p[4]!.dy, p[5]!.dx, p[5]!.dy) ..lineTo(host.right, host.top) ..lineTo(host.right, host.bottom) ..lineTo(host.left, host.bottom) ..close(); } }