Flutter滚动定位插件scroll_positioned的使用
Flutter滚动定位插件scroll_positioned的使用
Scroll Positioned
允许你在滚动时将小部件固定在指定位置。它可以相对于父小部件或绝对位置进行定位。
最佳使用场景是与可滚动的小部件(如 ListView
)结合使用,但也可以单独使用。
此插件使用了 Positioned
和 Stack
小部件。如果没有 Stack
,它会自动创建一个。
对于绝对位置,此插件使用了 Transform.translate
小部件。
特性
- 将小部件固定在滚动视图中。
- 支持水平和垂直滚动。
- 可以设置相对于顶部、左侧、右侧或底部的位置。
- 可以设置大小。
- 使用
ScrollPositioned.extend
可以扩展小部件相对于父小部件。 - 使用
ScrollPositioned.absolute
可以设置小部件的绝对位置。
开始使用
安装插件:
dart pub add scroll_positioned
导入包:
import 'package:scroll_positioned/scroll_positioned.dart';
使用方法
要将小部件固定在当前位置,只需这样做:
ScrollPositioned(
child: ...,
);
设置相对于父小部件的顶部、左侧、右侧或底部位置:
ScrollPositioned(
top: 50,
left: 0,
right: 0,
child: ...,
);
通过 width
和 height
设置小部件的大小,或者使用 ScrollPositioned.extend
相对于父小部件进行扩展:
ScrollPositioned(
width: 100,
height: 100,
child: ...,
);
ScrollPositioned.extend(
child: ...,
);
将小部件定位到绝对位置,不能同时设置底部和右侧:
ScrollPositioned.absolute(
top: 100,
left: 100,
child: ...,
);
为了灵活性,可以传递一个滚动控制器。如果不传递,则会尝试查找一个:
final scrollController = ScrollController();
ScrollPositioned(
controller: scrollController,
child: ...,
);
在某些情况下,当使用 ScrollPositioned.absolute
并且父小部件的子元素大小发生变化时,可能会导致小部件移动位置。除非重新构建,否则绝对位置不会更新。
示例
示例1
点击以在 DartPad 上互动
final size = MediaQuery.of(context).size;
ListView(
children: [
const SizedBox(
height: 300,
child: ScrollPositioned.expand(
child: Center(child: FlutterLogo(size: 150)),
),
),
Container(
height: size.height - 24 - 15 * 2,
padding: const EdgeInsets.only(top: 30),
margin: const EdgeInsets.only(left: 15, right: 15, bottom: 15),
decoration: BoxDecoration(
color: const Color(0xff30B3EE),
borderRadius: BorderRadius.circular(30),
),
child: const Center(
child: Opacity(
opacity: 0.9,
child: ColorFiltered(
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
child: FlutterLogo(size: 100),
),
)
),
),
],
);
示例2
点击以在 DartPad 上互动
final size = MediaQuery.of(context).size;
ListView(
children: [
Container(height: size.height * 0.25, color: Colors.cyan),
SizedBox(
height: size.height * 0.5,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(child: FlutterLogo(size: 200)),
),
),
Container(height: size.height * 0.25, color: Colors.blue),
Container(height: size.height * 0.25, color: Colors.green),
SizedBox(
height: size.height * 0.5,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(
child: ColorFiltered(
colorFilter: ColorFilter.mode(Colors.green, BlendMode.srcIn),
child: FlutterLogo(size: 200),
)
),
),
),
Container(height: size.height * 0.25, color: Colors.green),
Container(height: size.height * 0.25, color: Colors.red),
SizedBox(
height: size.height * 0.5,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(
child: ColorFiltered(
colorFilter: ColorFilter.mode(Colors.red, BlendMode.srcIn),
child: FlutterLogo(size: 200),
)
),
),
),
Container(height: size.height * 0.25, color: Colors.red),
Container(height: size.height * 0.25, color: Colors.cyan),
SizedBox(
height: size.height * 0.5,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(child: FlutterLogo(size: 200)),
),
),
Container(height: size.height * 0.25, color: Colors.blue),
],
);
示例3
点击以在 DartPad 上互动
final size = MediaQuery.of(context).size;
ListView(
children: [
SizedBox(height: (size.height - 200) / 2),
SizedBox(
height: 200,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(child: FlutterLogo(size: 200)),
),
),
SizedBox(
height: 150,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(
child: ColorFiltered(
colorFilter: ColorFilter.mode(Colors.green, BlendMode.srcIn),
child: FlutterLogo(size: 200),
),
),
),
),
SizedBox(
height: 150,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(
child: ColorFiltered(
colorFilter: ColorFilter.mode(Colors.red, BlendMode.srcIn),
child: FlutterLogo(size: 200),
),
),
),
),
SizedBox(
height: 150,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(
child: ColorFiltered(
colorFilter: ColorFilter.mode(Colors.orange, BlendMode.srcIn),
child: FlutterLogo(size: 200),
),
),
),
),
SizedBox(
height: 150,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(
child: ColorFiltered(
colorFilter: ColorFilter.mode(Colors.yellow, BlendMode.srcIn),
child: FlutterLogo(size: 200),
),
),
),
),
SizedBox(
height: 200,
child: ScrollPositioned.absolute(
width: size.width,
height: size.height,
child: const Center(child: FlutterLogo(size: 200)),
),
),
SizedBox(height: (size.height - 200) / 2),
],
);
小贴士
要为它添加动画效果,可以使用滚动控制器 (ScrollController
),animateTo
方法和 Timer.periodic
:
ScrollController? controller;
[@override](/user/override)
void initState() {
controller = ScrollController();
bool tick = false;
Timer.periodic(const Duration(seconds: 4), (timer) {
tick = !tick;
controller?.animateTo(
tick ? controller?.position.maxScrollExtent ?? 0 : 0,
duration: const Duration(seconds: 3),
curve: Curves.easeInOut,
);
});
super.initState();
}
更多关于Flutter滚动定位插件scroll_positioned的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter滚动定位插件scroll_positioned的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中,scroll_positioned
插件可以帮助你在滚动视图(如 ListView
或 CustomScrollView
)中定位到特定的子项。虽然 scroll_positioned
不是一个官方的 Flutter 插件,但假设你指的是如何在 Flutter 中实现滚动定位功能,我们可以使用 Flutter 自带的 ScrollController
和一些自定义逻辑来实现。
以下是一个使用 ScrollController
和 ScrollPositionWithOffset
来实现滚动定位的代码示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Scroll Position Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ScrollPositionDemo(),
);
}
}
class ScrollPositionDemo extends StatefulWidget {
@override
_ScrollPositionDemoState createState() => _ScrollPositionDemoState();
}
class _ScrollPositionDemoState extends State<ScrollPositionDemo> {
final ScrollController _scrollController = ScrollController();
final List<String> _items = List<String>.generate(100, (i) => "Item $i");
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _scrollToIndex(int index) {
// 计算目标位置,假设每个item的高度是50
final double itemHeight = 50.0;
final double targetOffset = index * itemHeight;
_scrollController.animateTo(
targetOffset,
duration: Duration(milliseconds: 300),
curve: Curves.ease,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Scroll Position Demo'),
),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_items[index]),
);
},
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<int>.generate(10, (i) => i * 10)
.map<Widget>((int index) {
return ElevatedButton(
onPressed: () => _scrollToIndex(index),
child: Text("Scroll to Item ${index}"),
);
}).toList(),
),
],
),
);
}
}
在这个示例中,我们创建了一个包含100个项目的 ListView
。每个项目的高度假设为50。我们提供了一个 ScrollController
来控制滚动视图,并通过 _scrollToIndex
方法计算并滚动到指定的项目。
注意:
- 实际应用中,如果每个项目的高度不同,你可能需要使用
RenderBox
或GlobalKey
来获取每个项目的实际高度。 scroll_positioned
插件如果存在,通常会有更简洁的API来处理滚动定位,但Flutter社区和官方文档中并没有直接提及这个名称,因此上述示例展示了使用核心功能实现相同效果的方法。
希望这个示例能帮助你理解如何在Flutter中实现滚动定位功能。