我有一个有状态的小部件类,其中我有一个 SingleChildScrollView,它采用 Column 和一些小部件,假设 w1、w2、w3、w4 和 w5 都是可滚动的,我想要实现的是当用户向上滚动屏幕 w1、w2 ,w4,w5 应该按预期运行,但是 w3 应该在到达固定位置时保持不变,比如(屏幕高度 - 50)。
这是我的代码,我能够获得位置并添加了一个标志“_isStuck”,现在我需要在标志变为真时粘贴 w3 小部件,否则当标志为假时它应该随流程滚动。
`import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final GlobalKey _key = GlobalKey();
ScrollController _controller = ScrollController();
bool _isStuck = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
}
void _afterLayout(_) {
_controller.addListener(
() {
final RenderBox renderBox =
_key.currentContext!.findRenderObject() as RenderBox;
final Offset offset = renderBox.localToGlobal(Offset.zero);
final double startY = offset.dy;
if (startY <= 120) {
setState(() {
_isStuck = true;
});
} else {
setState(() {
_isStuck = false;
});
}
print("Check position: - $startY - $_isStuck");
},
);
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
controller: _controller,
child: Column(
children: [
Container(
height: 400,
color: Colors.red,
child: const Text('w1'),
),
Container(
height: 400,
color: Colors.green,
child: const Text('w2'),
),
RepaintBoundary(
child: Container(
height: 100,
color: Colors.blue.shade400,
key: _key,
child: const Text('w3'),
),
),
Container(
height: 500,
color: Colors.yellow,
child: const Text('w4'),
),
Container(
height: 500,
color: Colors.orange,
child: const Text('w5'),
),
],
),
);
}
}
首先,创建一个
Stack
。在SingleChildScrollView
中添加一个Stack
作为第一项。接下来,添加一个 Positioned
小部件,将 w3
作为其子项作为 Stack
中的第二项。这个Positioned
小部件只有在_isStuck
为真时才会被渲染。
在
SingleChildScrollView
小部件内,您也将拥有 w3
小部件,但只有在 _isStuck
为 false 时才可见。
这是代码。
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final GlobalKey _key = GlobalKey();
final ScrollController _controller = ScrollController();
bool _isStuck = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
}
void _afterLayout(_) {
_controller.addListener(
() {
final RenderBox renderBox =
_key.currentContext!.findRenderObject() as RenderBox;
final Offset offset = renderBox.localToGlobal(Offset.zero);
final double startY = offset.dy;
setState(() {
_isStuck = startY <= 120;
});
print("Check position: - $startY - $_isStuck");
},
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
SingleChildScrollView(
controller: _controller,
child: Column(
children: [
Container(
height: 400,
color: Colors.red,
child: const Text('w1'),
),
Container(
height: 400,
color: Colors.green,
child: const Text('w2'),
),
if (!_isStuck) _w3(),
Container(
height: 500,
color: Colors.yellow,
child: const Text('w4'),
),
Container(
height: 500,
color: Colors.orange,
child: const Text('w5'),
),
],
),
),
if (_isStuck)
Positioned(
top: MediaQuery.of(context).size.height - 50,
left: 0,
right: 0,
child: _w3(),
),
],
);
}
Widget _w3() {
return RepaintBoundary(
child: Container(
height: 100,
color: Colors.blue.shade400,
child: const Text('w3'),
key: _key,
),
);
}
}