| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- import 'package:flutter/material.dart';
- import 'package:flutter_screenutil/flutter_screenutil.dart';
- import 'package:flutter_sticky_header/flutter_sticky_header.dart';
- import 'package:get/get.dart';
- import 'package:nomo/app/widgets/click_opacity.dart';
- import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
- import '../../../constants/iconfont/iconfont.dart';
- import '../../../data/models/launch/groups.dart';
- import '../../../widgets/country_icon.dart';
- import '../../home/controllers/home_controller.dart';
- import '../controllers/node_controller.dart';
- class NodeList extends StatefulWidget {
- final int tabIndex;
- const NodeList({super.key, required this.tabIndex});
- @override
- State<NodeList> createState() {
- return _NodeListState();
- }
- }
- class _NodeListState extends State<NodeList>
- with AutomaticKeepAliveClientMixin {
- @override
- bool get wantKeepAlive => true;
- // 获取 Controller
- NodeController get controller => Get.find<NodeController>();
- /// 获取国家的展开状态
- bool _getExpandedState(String countryCode) {
- return controller.getExpandedState(widget.tabIndex, countryCode);
- }
- /// 设置国家的展开状态
- void _setExpandedState(String countryCode, bool expanded) {
- setState(() {
- controller.setExpandedState(widget.tabIndex, countryCode, expanded);
- });
- }
- @override
- Widget build(BuildContext context) {
- super.build(context);
- // 获取当前 tab 的数据
- final data = controller.getDataByTabIndex(widget.tabIndex);
- if (data == null || data.tags == null || data.list == null) {
- return Center(
- child: Text(
- '暂无数据',
- style: TextStyle(fontSize: 16.sp, color: Get.reactiveTheme.hintColor),
- ),
- );
- }
- // 按 tag 分组数据
- final groupedData = <int, List<dynamic>>{};
- for (var location in data.list!) {
- if (location.tag != null) {
- groupedData.putIfAbsent(location.tag!, () => []).add(location);
- }
- }
- // 创建 tag 名称映射
- final tagNameMap = <int, String>{};
- for (var tag in data.tags!) {
- if (tag.id != null && tag.name != null) {
- tagNameMap[tag.id!] = tag.name!;
- }
- }
- // 按 sort 排序 tags
- final sortedTags = data.tags!.toList()
- ..sort((a, b) => (a.sort ?? 0).compareTo(b.sort ?? 0));
- return CustomScrollView(
- slivers: [
- for (var tag in sortedTags)
- if (groupedData.containsKey(tag.id) &&
- groupedData[tag.id]!.isNotEmpty)
- SliverStickyHeader(
- header: Container(
- color: Get.reactiveTheme.scaffoldBackgroundColor,
- padding: const EdgeInsets.all(16),
- child: Text(
- tag.name ?? '',
- style: TextStyle(
- color: Get.reactiveTheme.hintColor,
- fontSize: 16,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- sliver: SliverList(
- delegate: SliverChildBuilderDelegate((context, i) {
- final locationList = groupedData[tag.id]![i];
- final countryCode = locationList.icon ?? '';
- return _CountrySection(
- locationList: locationList,
- // 传递展开状态
- expanded: _getExpandedState(countryCode),
- // 展开状态变化回调
- onExpandedChanged: (expanded) {
- _setExpandedState(countryCode, expanded);
- },
- );
- }, childCount: groupedData[tag.id]!.length),
- ),
- ),
- SliverSafeArea(sliver: SliverToBoxAdapter(child: 0.verticalSpace)),
- ],
- );
- }
- }
- class _CountrySection extends StatelessWidget {
- final LocationList locationList;
- final bool expanded;
- final ValueChanged<bool> onExpandedChanged;
- const _CountrySection({
- required this.locationList,
- required this.expanded,
- required this.onExpandedChanged,
- });
- @override
- Widget build(BuildContext context) {
- final countryIcon = locationList.icon ?? '';
- final countryName = locationList.name ?? '';
- final locations = locationList.locations ?? [];
- // 获取 HomeController 并判断当前国家是否有选中的节点
- final homeController = Get.find<HomeController>();
- final hasSelectedLocation = locations.any(
- (loc) => loc.id == homeController.selectedLocation?.id,
- );
- // 根据展开状态和是否选中设置背景色
- final backgroundColor = hasSelectedLocation
- ? (expanded
- ? Get.reactiveTheme.cardColor
- : Get.reactiveTheme.highlightColor)
- : Get.reactiveTheme.highlightColor;
- return AnimatedContainer(
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeInOut,
- margin: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.w),
- decoration: BoxDecoration(
- color: backgroundColor,
- borderRadius: BorderRadius.circular(12.r),
- ),
- child: Column(
- children: [
- ClickOpacity(
- onTap: () => onExpandedChanged(!expanded),
- child: Padding(
- padding: EdgeInsets.all(14.w),
- child: Row(
- children: [
- // 国旗图标
- CountryIcon(
- countryCode: countryIcon,
- width: 32.w,
- height: 24.w,
- borderRadius: 4.r,
- ),
- 10.horizontalSpace,
- Text(
- countryName,
- style: TextStyle(
- fontSize: 16.sp,
- height: 1.5,
- fontWeight: FontWeight.w500,
- color: hasSelectedLocation
- ? Get.reactiveTheme.primaryColor
- : Get.reactiveTheme.textTheme.bodyLarge!.color,
- ),
- ),
- const Spacer(),
- // 箭头图标
- AnimatedRotation(
- turns: expanded ? 0.25 : 0.0,
- duration: const Duration(milliseconds: 300),
- child: Icon(
- IconFont.icon02,
- size: 20.w,
- color: Get.reactiveTheme.hintColor,
- ),
- ),
- ],
- ),
- ),
- ),
- ClipRect(
- child: AnimatedAlign(
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeInOut,
- heightFactor: expanded ? 1.0 : 0.0,
- alignment: Alignment.topLeft,
- child: AnimatedOpacity(
- opacity: expanded ? 1.0 : 0.0,
- duration: const Duration(milliseconds: 300),
- child: Column(
- children: locations.map((location) {
- final locationName = location.name ?? '';
- final locationIcon = location.icon ?? countryIcon;
- // 判断当前节点是否被选中
- final isSelected =
- location.id == homeController.selectedLocation?.id;
- return ClickOpacity(
- onTap: () {
- // 获取 HomeController
- final homeController = Get.find<HomeController>();
- homeController.selectLocation(location);
- homeController.handleConnect(delay: true);
- // 返回上一页
- Get.back();
- },
- child: Column(
- children: [
- Divider(
- height: 1,
- color: Get.reactiveTheme.dividerColor,
- ),
- Container(
- alignment: Alignment.centerLeft,
- margin: EdgeInsets.only(
- top: 14.w,
- bottom: 14.w,
- left: 24.w,
- right: 14.w,
- ),
- child: Row(
- children: [
- CountryIcon(
- countryCode: locationIcon,
- width: 32.w,
- height: 24.w,
- borderRadius: 4.r,
- ),
- 10.horizontalSpace,
- Expanded(
- child: Text(
- locationName,
- style: TextStyle(
- fontSize: 14.sp,
- fontWeight: FontWeight.w500,
- color: isSelected
- ? Get.reactiveTheme.primaryColor
- : Get
- .reactiveTheme
- .textTheme
- .bodyLarge!
- .color,
- ),
- ),
- ),
- // 显示延迟
- if (isSelected)
- Icon(
- IconFont.icon27,
- size: 16.w,
- color: Get.reactiveTheme.primaryColor,
- ),
- ],
- ),
- ),
- ],
- ),
- );
- }).toList(),
- ),
- ),
- ),
- ),
- ],
- ),
- );
- }
- }
|