import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:flutter_svg/flutter_svg.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/assets.dart'; import '../controllers/node_controller.dart'; class NodeList extends StatefulWidget { final int tabIndex; const NodeList({super.key, required this.tabIndex}); @override State createState() { return _NodeListState(); } } class _NodeListState extends State with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; // 获取 Controller NodeController get controller => Get.find(); /// 获取国家的展开状态 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); final data = { 'Europe': [ { 'title': 'United Kingdom', 'code': 'GB', 'cities': [ 'London', 'Edinburgh', 'Cardiff', 'Liverpool', 'Manchester', ], }, { 'title': 'Italy', 'code': 'IT', 'cities': ['Rome', 'Milan', 'Naples', 'Turin', 'Genoa'], }, { 'title': 'Germany', 'code': 'DE', 'cities': ['Berlin', 'Hamburg', 'Munich', 'Frankfurt', 'Cologne'], }, ], 'Asia': [ { 'title': 'China', 'code': 'CN', 'cities': ['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen', 'Chengdu'], }, { 'title': 'Japan', 'code': 'JP', 'cities': ['Tokyo', 'Osaka', 'Nagoya', 'Sapporo', 'Fukuoka'], }, { 'title': 'Korea', 'code': 'KR', 'cities': ['Seoul', 'Busan', 'Incheon', 'Daegu', 'Gwangju'], }, { 'title': 'India', 'code': 'IN', 'cities': ['Mumbai', 'Delhi', 'Bangalore', 'Chennai', 'Hyderabad'], }, ], 'America': [ { 'title': 'United States', 'code': 'US', 'cities': ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Miami'], }, { 'title': 'Canada', 'code': 'CA', 'cities': ['Toronto', 'Montreal', 'Vancouver', 'Calgary', 'Edmonton'], }, { 'title': 'Mexico', 'code': 'MX', 'cities': [ 'Mexico City', 'Guadalajara', 'Monterrey', 'Tijuana', 'Puebla', ], }, { 'title': 'Brazil', 'code': 'BR', 'cities': [ 'Sao Paulo', 'Rio de Janeiro', 'Brasilia', 'Salvador', 'Fortaleza', ], }, ], }; return CustomScrollView( slivers: [ for (var region in data.entries) SliverStickyHeader( header: Container( color: Get.reactiveTheme.scaffoldBackgroundColor, padding: const EdgeInsets.all(16), child: Text( region.key, style: TextStyle( color: Get.reactiveTheme.hintColor, fontSize: 16, fontWeight: FontWeight.bold, ), ), ), sliver: SliverList( delegate: SliverChildBuilderDelegate((context, i) { final country = region.value[i]; final countryCode = country['code'] as String; final cities = country['cities'] as List; return _CountrySection( title: country['title'] as String, code: countryCode, cities: cities, // 传递展开状态 expanded: _getExpandedState(countryCode), // 展开状态变化回调 onExpandedChanged: (expanded) { _setExpandedState(countryCode, expanded); }, ); }, childCount: region.value.length), ), ), ], ); } } class _CountrySection extends StatelessWidget { final String title; final String code; final List cities; final bool expanded; final ValueChanged onExpandedChanged; const _CountrySection({ required this.title, required this.code, required this.cities, required this.expanded, required this.onExpandedChanged, }); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ ClickOpacity( onTap: () => onExpandedChanged(!expanded), child: Padding( padding: EdgeInsets.all(14.w), child: Row( children: [ // 国旗图标 ClipRRect( borderRadius: BorderRadius.circular(4.r), // 设置圆角 child: SvgPicture.asset( Assets.getCountryFlagImage(code), width: 32.w, height: 24.w, fit: BoxFit.cover, // placeholderBuilder: (context) => Container( // width: 32.w, // height: 24.w, // decoration: BoxDecoration( // borderRadius: BorderRadius.circular(4.r), // color: Colors.grey[200], // ), // alignment: Alignment.center, // child: Icon( // Icons.flag, // size: 16.w, // color: Colors.grey[400], // ), // ), ), ), 10.horizontalSpace, Text( title, style: TextStyle( fontSize: 16.sp, height: 1.5, fontWeight: FontWeight.w500, color: Get.reactiveTheme.textTheme.bodyLarge!.color, ), ), const Spacer(), // 箭头图标 AnimatedRotation( turns: expanded ? 0.25 : 0.0, duration: const Duration(milliseconds: 300), child: Icon( Icons.keyboard_arrow_right, 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: cities.map((city) { final cityName = city as String; return ClickOpacity( onTap: () {}, 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: [ ClipRRect( borderRadius: BorderRadius.circular( 4.r, ), // 设置圆角 child: SvgPicture.asset( Assets.getCountryFlagImage(code), width: 32.w, height: 24.w, fit: BoxFit.cover, // placeholderBuilder: (context) => Container( // width: 32.w, // height: 24.w, // decoration: BoxDecoration( // borderRadius: BorderRadius.circular(4.r), // color: Colors.grey[200], // ), // alignment: Alignment.center, // child: Icon( // Icons.flag, // size: 16.w, // color: Colors.grey[400], // ), // ), ), ), 10.horizontalSpace, Text( cityName, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, color: Get.reactiveTheme.hintColor, ), ), ], ), ), ], ), ); }).toList(), ), ), ), ), ], ), ); } }