Flutter如何滚动到SliverList中的特定项目位置?
Posted
技术标签:
【中文标题】Flutter如何滚动到SliverList中的特定项目位置?【英文标题】:Flutter how to scroll to a specific item position in SliverList? 【发布时间】:2020-06-17 02:36:04 【问题描述】:我有一个SliverList
和SliverChildBuilderDelegate
,我的问题是我无法将ScrollController
设置为SliverList
,因为我想控制列表的滚动,我正在寻找滚动到列表中的特定项目(这是非常大的)和正常的ListView
,这项工作很容易,我想知道为什么SliverList
中没有controller
属性,比如ListView
?
注意:我知道CustomScrollView
有controller
属性并且我已经测试过,但这将控制整个视口而不仅仅是列表
@override
Widget build(BuildContext context)
MainState m = Provider.of<MainState>(context, listen: false);
return DefaultTabController(
length: m.itemCategoryList.length,
child: Scaffold(
// appBar: _createAppBar(),
body: Builder(builder: (context) => _createBody(context))),
);
AppBar _createAppBar()
MainState m = Provider.of<MainState>(context);
return AppBar(
title: Text(
m.selectedPartner.fullname,
style: Theme.of(context).textTheme.display3,
),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: ()
Navigator.pop(context);
,
),
);
_createBody(BuildContext context)
MainState m = Provider.of<MainState>(context);
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
titleSpacing: 0.0,
elevation: 20.0,
title: Container(
decoration: BoxDecoration(color: Colors.black.withOpacity(0.3)),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: ()
Navigator.pop(context);
,
),
Spacer(
flex: 2,
),
IconButton(
icon: Icon(Icons.info),
onPressed: () ,
),
IconButton(
icon: Icon(Icons.search),
onPressed: () ,
),
],
),
],
),
),
centerTitle: false,
automaticallyImplyLeading: false,
pinned: true,
bottom: PreferredSize(
// Add this code
preferredSize: Size.fromHeight(10.0), // Add this code
child: Text(''), // Add this code
),
expandedHeight: 250,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
collapseMode: CollapseMode.none,
background: Stack(
children: <Widget>[
Image.network(
m.selectedPartner.imagepath,
fit: BoxFit.cover,
height: 300.0,
width: MediaQuery.of(context).size.width,
color: Colors.black.withOpacity(0.6),
colorBlendMode: BlendMode.darken,
),
Align(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
m.selectedPartner.fullname,
style: Theme.of(context)
.textTheme
.headline
.copyWith(color: Colors.white),
),
Text(
m.selectedPartner.categorytexts,
style: Theme.of(context)
.textTheme
.title
.copyWith(fontSize: 17.0, color: Colors.white),
),
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 30.0,
decoration: BoxDecoration(color: Colors.white),
child: Row(
children: <Widget>[
Text(
"Delivery time :" +
m.selectedPartner.deliverytime.toString() +
" mins",
style: Theme.of(context).textTheme.subtitle)
],
),
),
)
],
)),
),
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
TabBar(
isScrollable: true,
labelColor: Colors.black87,
unselectedLabelColor: Colors.black,
tabs: m.itemCategoryList
.map((ele) => Tab(
text: ele.itemcategoryname,
))
.toList(),
),
),
pinned: true,
),
//=========I want to scroll to a specific position in this list
GroupedSliverList(
elements: m.itemList,
groupBy: (ItemModel item) => item.itemcategory_id,
groupSeparatorBuilder: (i, item) => _createGroupItem(i, item),
itemBuilder: (_, item) => _createItem(item)),
],
);
_createGroupItem(int categId, ItemModel item)
MainState m = Provider.of<MainState>(context, listen: false);
ItemCategoryModel categModel =
m.itemCategoryList.firstWhere((ele) => ele.id == categId);
return Container(
child: Text(
categModel.itemcategoryname,
style: Theme.of(context).textTheme.headline,
),
);
_createItem(ItemModel item)
return Card(
elevation: 3.0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
CircleAvatar(
radius: 30.0,
backgroundImage: NetworkImage(item.imagepath),
),
SizedBox(
width: 18.0,
),
_createDetailColumn(item)
],
),
),
);
_createDetailColumn(ItemModel item)
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
flex: 1,
child: Row(
children: <Widget>[
Expanded(
child: Text(
item.itemname,
style: Theme.of(context).textTheme.title,
),
),
],
)),
SizedBox(
height: 5.0,
),
Flexible(
flex: 1,
child: Row(
children: <Widget>[
Expanded(
child: Text(
item.description,
style: Theme.of(context).textTheme.subtitle,
),
),
],
),
),
SizedBox(
height: 5.0,
),
SizedBox(
height: 8.0,
),
Row(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 1.0),
borderRadius: BorderRadius.circular(10.0)),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(item.price.toString() + " L.E.",
style: Theme.of(context)
.textTheme
.subtitle
.copyWith(color: Colors.green, fontSize: 16.0)),
),
),
],
)
],
),
);
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate
_SliverAppBarDelegate(this._tabBar);
final TabBar _tabBar;
@override
double get minExtent => _tabBar.preferredSize.height;
@override
double get maxExtent => _tabBar.preferredSize.height;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent)
return new Container(
color: Colors.yellow,
child: _tabBar,
);
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate)
return false;
【问题讨论】:
自定义 SliverList 哪些组列表项 你做到了吗? 我也有同样的问题。有人找到解决办法了吗? 【参考方案1】:你可以用这个:
-
为您的特定项目位置创建一个全局键,如下所示
final dataKey = new GlobalKey();
将键 key: dataKey
添加到此特定位置
添加使页面滚动到您的按钮Scrollable.ensureVisible(dataKey.currentContext)
的命令
这是滚动到最后一项的完整代码:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Scroll To Index Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SilverAppBarExample(),
);
class SilverAppBarExample extends StatelessWidget
final dataKey = new GlobalKey();
@override
Widget build(BuildContext context)
return Scaffold(
body: Container(
// width: MediaQuery.of(context).size.width,
// height: MediaQuery.of(context).size.height,
child: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: IconButton(
icon: Icon(Icons.filter_1),
onPressed: ()
// Do something
),
expandedHeight: 220.0,
floating: true,
pinned: true,
snap: true,
elevation: 50,
backgroundColor: Colors.pink,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text('Title',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Image.network(
'https://media-exp1.licdn.com/dms/image/C4D03AQEs7z0qHy2IWA/profile-displayphoto-shrink_400_400/0/1587196263121?e=1611792000&v=beta&t=bGjaklbEtTDYOFk5UvGLOTO5N3pSvIJtq6javEp32lU',
fit: BoxFit.cover,
),
),
),
new SliverList(
delegate: new SliverChildListDelegate([
Column(
children: [
_buildList(50),
Card(
key: dataKey,
child: new Text("data\n\n\n\n\n\ndata"),
),
],
),
]),
),
],
),
),
bottomNavigationBar: new RaisedButton(
onPressed: () => Scrollable.ensureVisible(dataKey.currentContext),
child: new Text("Scroll to data"),
),
);
Widget _buildList(int count)
List<Widget> listItems = List();
for (int i = 0; i < count; i++)
listItems.add(
new Padding(
padding: new EdgeInsets.all(20.0),
child: new Text(
'Item $i.toString()',
style: new TextStyle(fontSize: 25.0),
),
),
);
return Column(
children: listItems,
);
滚动前的页面:
滚动后的页面:
【讨论】:
这很有帮助。但并不完全满足能够按照操作要求滚动到列表中的特定项目 你在这里滥用 SliverList。您的实现不是虚拟的,在大多数情况下不起作用。【参考方案2】:您是否尝试过使用infinite listview? 我要自己试试这个。看起来很有希望。
import 'package:flutter/material.dart';
import 'package:infinite_listview/infinite_listview.dart';
void main() => runApp(ExampleApp());
class ExampleApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ExampleScreen(),
);
class ExampleScreen extends StatefulWidget
@override
_ExampleScreenState createState() => _ExampleScreenState();
class _ExampleScreenState extends State<ExampleScreen>
final InfiniteScrollController _infiniteController = InfiniteScrollController(
initialScrollOffset: 0.0,
);
@override
Widget build(BuildContext context)
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: const Text('Example'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_downward),
onPressed: ()
_infiniteController.animateTo(_infiniteController.offset + 2000.0,
duration: const Duration(milliseconds: 250), curve: Curves.easeIn);
,
),
IconButton(
icon: const Icon(Icons.arrow_upward),
onPressed: ()
_infiniteController.animateTo(_infiniteController.offset - 2000.0,
duration: const Duration(milliseconds: 250), curve: Curves.easeIn);
,
),
],
bottom: const TabBar(
tabs: <Widget>[
Tab(text: 'First'),
Tab(text: 'Second'),
Tab(text: 'Third'),
],
),
),
body: TabBarView(
children: <Widget>[
_buildTab(0),
_buildTab(1),
_buildTab(2),
],
),
),
);
Widget _buildTab(int tab)
return InfiniteListView.separated(
key: PageStorageKey(tab),
controller: _infiniteController,
itemBuilder: (BuildContext context, int index)
return Material(
child: InkWell(
onTap: () ,
child: ListTile(
title: Text('Tab $tab Item #$index'),
subtitle: Text('Subtitle $index'),
trailing: const Icon(Icons.chevron_right),
),
),
);
,
separatorBuilder: (BuildContext context, int index) => const Divider(height: 2.0),
anchor: 0.5,
);
【讨论】:
【参考方案3】:您可以使用此插件 scrollable_positioned_list 来执行滚动事件,但不能将其与 silverList 一起使用
【讨论】:
以上是关于Flutter如何滚动到SliverList中的特定项目位置?的主要内容,如果未能解决你的问题,请参考以下文章
Flutter SliverAppBar 和 SliverList 有不同的滚动条
如何为 Flutter SliverList 中的元素设置动画?