这是一个有三个主要小部件的应用程序:大小框和两个可滚动的ListView
,一个在SizedBox()
中,另一个在Expanded()
中。
我想做的是让底部列表在滚动时向上移动并占据顶部列表的位置,如果顶部列表滚动则移动到底部列表的原始位置。我该如何做到这一点?
绝笔:
import 'package:flutter/material.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(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('two scrolls')),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Widget1: this box should stay in place
SizedBox(
height: MediaQuery.of(context).size.height / 5,
child: Container(
color: Colors.purple,
),
),
// TopList: this box should shrink to the top until it hits some minimum height, on scroll in BottomList
// If it is scrolled after Widget3 took all the space, expand its contents to bottom to a set height.
SizedBox(
// has half of remaining height
height: MediaQuery.of(context).size.height / 2 - MediaQuery.of(context).size.height / 5 - kToolbarHeight,
child: ListView(
children: [
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
],
),
),
const Divider(height: 16, thickness: 0.5, color: Colors.black),
// BottomList: on scroll, should expand upwards to fill the space of TopList until it hits TopList_minHeight, then scroll as usual.
// On scroll of TopList, should move to it's original position.
Expanded(
child: ListView(
children: [
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
],
),
)
],
),
);
}
}
您可以向ScrollController
添加侦听器,以调整Top列表小部件的高度。注意,在这种情况下,您需要手动处置ScrollController。
import 'package:flutter/material.dart';
import 'dart:math';
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(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final ScrollController topScrollController;
late final ScrollController bottomScrollController;
double topOffset = 0;
@override
void initState() {
super.initState();
topScrollController = ScrollController();
topScrollController.addListener(() {
setState(() {
topOffset = 0;
});
});
bottomScrollController = ScrollController();
bottomScrollController.addListener(() {
setState(() {
topOffset = bottomScrollController.offset;
});
});
}
@override
void dispose() {
topScrollController.dispose();
bottomScrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final topMaxHeight = MediaQuery.of(context).size.height / 2 -
MediaQuery.of(context).size.height / 5 -
kToolbarHeight;
const topMinHeight = 50.0;
double height = topOffset <= 200
? topMaxHeight
: max(topMaxHeight + 200 - topOffset, topMinHeight);
return Scaffold(
// appBar: AppBar(title: const Text('two scrolls')),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Widget1: this box should stay in place
SizedBox(
height: MediaQuery.of(context).size.height / 5,
child: Container(
color: Colors.purple,
),
),
// TopList: this box should shrink to the top until it hits some minimum height, on scroll in BottomList
// If it is scrolled after Widget3 took all the space, expand its contents to bottom to a set height.
SizedBox(
// has half of remaining height
height: height,
child: ListView(
controller: topScrollController,
children: [
Container(
width: 160.0,
height: 160.0,
color: Colors.green,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.purple,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.green,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.purple,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.green,
),
],
),
),
const Divider(height: 16, thickness: 0.5, color: Colors.black),
// BottomList: on scroll, should expand upwards to fill the space of TopList until it hits TopList_minHeight, then scroll as usual.
// On scroll of TopList, should move to it's original position.
Expanded(
child: ListView(
controller: bottomScrollController,
children: [
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
height: 160.0,
color: Colors.red,
),
],
),
)
],
),
);
}
}