导航栏

This commit is contained in:
2026-01-23 17:00:17 +08:00
parent 16bae6a1e9
commit 9fdca9136d
14 changed files with 107 additions and 100 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:getx_scaffold/common/index.dart'; import 'package:get/get.dart';
import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:ln_jq_app/common/login_util.dart';
import 'package:ln_jq_app/pages/c_page/car_info/view.dart'; import 'package:ln_jq_app/pages/c_page/car_info/view.dart';
import 'package:ln_jq_app/pages/c_page/map/view.dart'; import 'package:ln_jq_app/pages/c_page/map/view.dart';
import 'package:ln_jq_app/pages/c_page/mine/view.dart'; import 'package:ln_jq_app/pages/c_page/mine/view.dart';
@@ -9,9 +10,10 @@ import 'package:ln_jq_app/pages/c_page/reservation/view.dart';
import 'index.dart'; import 'index.dart';
class BaseWidgetsPage extends GetView<BaseWidgetsController> { class BaseWidgetsPage extends GetView<BaseWidgetsController> {
BaseWidgetsPage({super.key}); BaseWidgetsPage({super.key});
final PageController _pageController = PageController(); final PageController _pageController = PageController();
// 主视图 // 主视图
Widget _buildView() { Widget _buildView() {
return PageView( return PageView(
@@ -20,54 +22,68 @@ class BaseWidgetsPage extends GetView<BaseWidgetsController> {
onPageChanged: (index) { onPageChanged: (index) {
jumpTabAndPage(index); jumpTabAndPage(index);
}, },
children: _buildPages(), // 页面的列表 children: _buildPages(),
); );
} }
void jumpTabAndPage(int index) { void jumpTabAndPage(int index) {
controller.pageIndex = index; // 更新页面索引 controller.pageIndex = index;
controller.updateUi(); // 更新 UI controller.updateUi();
_pageController.jumpToPage(controller.pageIndex); _pageController.jumpToPage(controller.pageIndex);
} }
// 对应的页面
List<Widget> _buildPages() { List<Widget> _buildPages() {
return [ return [ReservationPage(), MapPage(), CarInfoPage(), MinePage()];
ReservationPage(),
MapPage(),
CarInfoPage(),
MinePage(),
];
} }
//导航栏 // 自定义导航栏 (悬浮胶囊样式)
Widget _buildNavigationBar() { Widget _buildNavigationBar() {
return NavigationX( return SafeArea(
currentIndex: controller.pageIndex, // 当前选中的tab索引 child: Container(
onTap: (index) { height: 50.h,
jumpTabAndPage(index); margin: const EdgeInsets.fromLTRB(24, 0, 24, 10), // 悬浮边距
}, // 切换tab事件 decoration: BoxDecoration(
items: [ color: Color.fromRGBO(240, 244, 247, 1), // 浅灰色背景
NavigationItemModel( borderRadius: BorderRadius.circular(30),
label: '加氢预约', boxShadow: [
icon: AntdIcon.orderedlist, BoxShadow(
selectedIcon: AntdIcon.calendar_fill, color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
), ),
NavigationItemModel( child: Row(
label: '地图', mainAxisAlignment: MainAxisAlignment.spaceAround,
icon: AntdIcon.location, children: [
selectedIcon: AntdIcon.location_fill, _buildNavItem(0, "ic_h2_select@2x", "ic_h2@2x"),
_buildNavItem(1, "ic_map_select@2x", "ic_map@2x"),
_buildNavItem(2, "ic_car_select@2x", "ic_car@2x"),
_buildNavItem(3, "ic_user_select@2x", "ic_user@2x"),
],
), ),
NavigationItemModel( ),
label: '车辆信息', );
icon: AntdIcon.car, }
selectedIcon: AntdIcon.car_fill,
// 构建单个导航项
Widget _buildNavItem(int index, String icon, String selectedIcon) {
bool isSelected = controller.pageIndex == index;
return GestureDetector(
onTap: () => jumpTabAndPage(index),
behavior: HitTestBehavior.opaque,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF006633) : Colors.transparent, // 选中时的深绿色背景
borderRadius: BorderRadius.circular(20),
), ),
NavigationItemModel( child: SizedBox(
label: '我的', height: 24,
icon: AntdIcon.user, width: 24,
selectedIcon: AntdIcon.user, child: LoginUtil.getAssImg(isSelected ? selectedIcon : icon),),
), ),
],
); );
} }
@@ -78,10 +94,10 @@ class BaseWidgetsPage extends GetView<BaseWidgetsController> {
id: 'baseWidgets', id: 'baseWidgets',
builder: (_) { builder: (_) {
return Scaffold( return Scaffold(
extendBody: false, extendBody: true, // 重要:让 body 延伸到导航栏后面
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
bottomNavigationBar: _buildNavigationBar(), bottomNavigationBar: _buildNavigationBar(),
body: SafeArea(child: _buildView()), body: _buildView(), // 移除 SafeArea 以获得更好的全屏沉浸感
); );
}, },
); );

View File

@@ -26,7 +26,7 @@ class CarInfoPage extends GetView<CarInfoController> {
children: [ children: [
_buildUserInfoCard(), _buildUserInfoCard(),
Padding( Padding(
padding: EdgeInsets.only(left: 20.w,right: 20.w), padding: EdgeInsets.only(left: 20.w, right: 20.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
@@ -35,6 +35,7 @@ class CarInfoPage extends GetView<CarInfoController> {
_buildCertificatesCard(context), _buildCertificatesCard(context),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildSafetyReminderCard(), _buildSafetyReminderCard(),
SizedBox(height: 95.h),
], ],
), ),
), ),
@@ -60,7 +61,7 @@ class CarInfoPage extends GetView<CarInfoController> {
child: Column( child: Column(
children: [ children: [
Padding( Padding(
padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 16, top: 40), padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 16, top: 50),
child: Row( child: Row(
children: [ children: [
Stack( Stack(
@@ -344,7 +345,7 @@ class CarInfoPage extends GetView<CarInfoController> {
), ),
const SizedBox(height: 9), const SizedBox(height: 9),
SizedBox( SizedBox(
height: 343.h, // 给定一个高度,或者使用别的方式布局 height: 333.h, // 给定一个高度,或者使用别的方式布局
child: TabBarView( child: TabBarView(
children: [ children: [
_buildCertificateContent('行驶证', controller.drivingAttachments), _buildCertificateContent('行驶证', controller.drivingAttachments),
@@ -362,66 +363,55 @@ class CarInfoPage extends GetView<CarInfoController> {
/// 构建单个证件的展示内容 /// 构建单个证件的展示内容
Widget _buildCertificateContent(String title, RxList<String> attachments) { Widget _buildCertificateContent(String title, RxList<String> attachments) {
return Obx(() { return Obx(() {
if (attachments.isEmpty) {
return const Center(child: Text('暂无相关证件信息'));
}
return Card( return Card(
elevation: 0, elevation: 0,
color: Colors.white, color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: EdgeInsets.all(16.0),
child: Column( child: attachments.isEmpty
crossAxisAlignment: CrossAxisAlignment.center, ? const Center(child: Text('暂无相关证件信息'))
mainAxisAlignment: MainAxisAlignment.center, : Column(
children: [ crossAxisAlignment: CrossAxisAlignment.center,
Row( mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ Row(
_buildCertDetailItem('所有人', '上海羚牛氢运物联网科技有限公司', isFull: true), mainAxisAlignment: MainAxisAlignment.spaceBetween,
_buildCertDetailItem('车辆识别代号', controller.vin), children: [
], _buildCertDetailItem('所有人', '上海羚牛氢运物联网科技有限公司', isFull: true),
), _buildCertDetailItem('车辆识别代号', controller.vin),
const SizedBox(height: 16), ],
Row( ),
mainAxisAlignment: MainAxisAlignment.spaceBetween, const SizedBox(height: 16),
children: [ Row(
_buildCertDetailItem( mainAxisAlignment: MainAxisAlignment.spaceBetween,
'有效期至', children: [
'2028-08-14', _buildCertDetailItem(
valueColor: const Color(0xFF52C41A), '有效期至',
), '2028-08-14',
_buildCertDetailItem('使用性质', '货运'), valueColor: const Color(0xFF52C41A),
], ),
), _buildCertDetailItem('使用性质', '货运'),
const SizedBox(height: 20), ],
// 附件预览部分 ),
Expanded( const SizedBox(height: 16),
child: ListView.builder( // 附件预览部分
scrollDirection: Axis.vertical, GestureDetector(
itemCount: attachments.length,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final url = attachments[index];
return GestureDetector(
onTap: () { onTap: () {
controller.openAttachment(url); controller.navigateToCertificateViewer(title, attachments);
}, },
child: Container( child: Container(
height: 184.h, height: 184.h,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: Color.fromRGBO(226, 232, 240, 1)), border: Border.all(color: Color.fromRGBO(226, 232, 240, 1)),
color: Color.fromRGBO(248, 250, 252, 1) color: Color.fromRGBO(248, 250, 252, 1),
), ),
child: Center(child: _buildAttachmentPreview(url)), child: Center(child: _buildAttachmentPreview(attachments[0])),
), ),
); ),
}, ],
), ),
),
],
),
), ),
); );
}); });

View File

@@ -38,7 +38,7 @@ class MinePage extends GetView<MineController> {
_buildSafetyReminderCard(), _buildSafetyReminderCard(),
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildLogoutButton(), _buildLogoutButton(),
SizedBox(height: 24.h), SizedBox(height: 95.h),
], ],
), ),
), ),

View File

@@ -37,6 +37,7 @@ class ReservationPage extends GetView<C_ReservationController> {
_buildReservationFormCard(context), _buildReservationFormCard(context),
const SizedBox(height: 5), const SizedBox(height: 5),
_buildTipsCard(), _buildTipsCard(),
SizedBox(height: 95.h),
], ],
), ),
), ),
@@ -474,7 +475,7 @@ class ReservationPage extends GetView<C_ReservationController> {
), ),
), ),
Obx( Obx(
() => DropdownButtonHideUnderline( () => DropdownButtonHideUnderline(
child: DropdownButton2<String>( child: DropdownButton2<String>(
isExpanded: true, isExpanded: true,
hint: const Text( hint: const Text(
@@ -485,15 +486,15 @@ class ReservationPage extends GetView<C_ReservationController> {
items: controller.stationOptions items: controller.stationOptions
.map( .map(
(station) => DropdownMenuItem<String>( (station) => DropdownMenuItem<String>(
value: station.hydrogenId, // value 是站点的唯一ID value: station.hydrogenId, // value 是站点的唯一ID
enabled: station.isSelect == 1, enabled: station.isSelect == 1,
child: _buildDropdownItem(station), // child 是自定义的 Widget child: _buildDropdownItem(station), // child 是自定义的 Widget
), ),
) )
.toList(), .toList(),
value: value:
// 当前的站点 处理默认 // 当前的站点 处理默认
controller.selectedStationId.value ?? controller.selectedStationId.value ??
(controller.stationOptions.isNotEmpty (controller.stationOptions.isNotEmpty
? controller.stationOptions.first.hydrogenId ? controller.stationOptions.first.hydrogenId
: null), : null),
@@ -506,15 +507,15 @@ class ReservationPage extends GetView<C_ReservationController> {
customButton: Obx(() { customButton: Obx(() {
// 优先从已选中的 ID 查找 // 优先从已选中的 ID 查找
var selectedStation = controller.stationOptions.firstWhereOrNull( var selectedStation = controller.stationOptions.firstWhereOrNull(
(s) => s.hydrogenId == controller.selectedStationId.value, (s) => s.hydrogenId == controller.selectedStationId.value,
); );
// 如果找不到已选中的(比如 ID 为空或列表里没有),并且列表不为空,则取第一个作为默认 // 如果找不到已选中的(比如 ID 为空或列表里没有),并且列表不为空,则取第一个作为默认
final stationToShow = final stationToShow =
selectedStation ?? selectedStation ??
(controller.stationOptions.isNotEmpty (controller.stationOptions.isNotEmpty
? controller.stationOptions.first ? controller.stationOptions.first
: null); : null);
// 如果有要显示的站点,就构建按钮 // 如果有要显示的站点,就构建按钮
if (stationToShow != null) { if (stationToShow != null) {