Closed feimenggo closed 1 month ago
Android, iOS, macOS, Windows
点击分组可以展开或收起列表,现在想要定位到列表的指定项。
import 'dart:math'; import 'package:flutter/material.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final scrollController = ScrollController(); late final observerController = SliverObserverController(controller: scrollController); final List<ItemBean> items = ItemBean.groupListData; Set<BuildContext> sliverContextSets = {}; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), floatingActionButton: FloatingActionButton( onPressed: () { // 滚动到指定位置 observerController.jumpTo(index: 5); }, tooltip: 'Increment', child: const Icon(Icons.gps_not_fixed_outlined), ), body: SliverViewObserver( controller: observerController, sliverContexts: () { return sliverContextSets.toList(); }, child: CustomScrollView( controller: scrollController, slivers: items.map((vo) { return SliverMainAxisGroup( slivers: [ SliverPersistentHeader( pinned: true, delegate: SliverHeaderDelegate.builder( maxHeight: 48, minHeight: 48, builder: (BuildContext context, _, __) { sliverContextSets.add(context); return buildGroup(vo); }, ), ), SliverFixedExtentList( delegate: SliverChildBuilderDelegate( (context, index) { sliverContextSets.add(context); return buildItem(vo.items[index]); }, childCount: vo.expand ? vo.items.length : 0, ), itemExtent: 56, ), ], ); }).toList(), ), ), ); } Widget buildGroup(ItemBean vo) { Widget child = const Icon(Icons.arrow_right, size: 12); if (vo.expand) child = Transform.rotate(angle: pi / 2, child: child); return Column( children: [ Expanded( child: GestureDetector( onTap: () { setState(() => vo.expand = !vo.expand); }, behavior: HitTestBehavior.translucent, child: Container( alignment: Alignment.centerLeft, color: Colors.white, child: Row( children: [ const SizedBox(width: 16), child, const SizedBox(width: 4), Expanded(child: Text(vo.groupName)), ], ), ), ), ), const Divider(indent: 20, endIndent: 20, height: 0), ], ); } Widget buildItem(String vo) { return Column( children: [ Expanded( child: Container( alignment: Alignment.centerLeft, padding: const EdgeInsets.symmetric(horizontal: 32), child: Text(vo), ), ), const Divider(indent: 20, endIndent: 20, height: 0), ], ); } } class ItemBean { final String groupName; final List<String> items; bool expand = false; ItemBean({required this.groupName, this.items = const []}); static List<ItemBean> get groupListData => [ ItemBean(groupName: '水果', items: [ '苹果', '香蕉', '橙子', '葡萄', '芒果', '梨', '桃子', '草莓', '西瓜', '柠檬', '菠萝', '樱桃', '蓝莓', '猕猴桃', '李子', '柿子', '杏', '杨梅', '石榴', '木瓜' ]), ItemBean(groupName: '动物', items: [ '狗', '猫', '狮子', '老虎', '大象', '熊', '鹿', '狼', '狐狸', '猴子', '企鹅', '熊猫', '袋鼠', '海豚', '鲨鱼', '斑马', '长颈鹿', '鳄鱼', '孔雀', '乌龟' ]), ItemBean(groupName: '职业', items: [ '医生', '护士', '教师', '工程师', '程序员', '律师', '会计', '警察', '消防员', '厨师', '司机', '飞行员', '科学家', '记者', '设计师', '作家', '演员', '音乐家', '画家', '摄影师' ]), ItemBean(groupName: '菜谱', items: [ '红烧肉', '糖醋排骨', '宫保鸡丁', '麻婆豆腐', '鱼香肉丝', '酸辣汤', '蒜蓉菠菜', '回锅肉', '水煮鱼', '烤鸭', '蛋炒饭', '蚝油生菜', '红烧茄子', '西红柿炒鸡蛋', '油焖大虾', '香菇鸡汤', '酸菜鱼', '麻辣香锅', '铁板牛肉', '干煸四季豆' ]), ]; } typedef SliverHeaderBuilder = Widget Function( BuildContext context, double shrinkOffset, bool overlapsContent); class SliverHeaderDelegate extends SliverPersistentHeaderDelegate { SliverHeaderDelegate({ required this.maxHeight, this.minHeight = 0, required Widget child, }) : builder = ((a, b, c) => child), assert(minHeight <= maxHeight && minHeight >= 0); SliverHeaderDelegate.fixedHeight({ required double height, required Widget child, }) : builder = ((a, b, c) => child), maxHeight = height, minHeight = height; SliverHeaderDelegate.builder({ required this.maxHeight, this.minHeight = 0, required this.builder, }); final double maxHeight; final double minHeight; final SliverHeaderBuilder builder; @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return SizedBox.expand( child: builder(context, shrinkOffset, overlapsContent)); } @override double get maxExtent => maxHeight; @override double get minExtent => minHeight; @override bool shouldRebuild(SliverHeaderDelegate oldDelegate) { return oldDelegate.maxExtent != maxExtent || oldDelegate.minExtent != minExtent || oldDelegate.builder != builder; } }
No response
Platforms
Android, iOS, macOS, Windows
Description
点击分组可以展开或收起列表,现在想要定位到列表的指定项。
My code
Try do it
No response