flutter - 颤动中的全屏GridView
问题描述
我想实现双向 Pageview 或全屏 Gridview。
即布局看起来像这样。
| | |
| | |
_____|_______|_______|____
| | |
| | |
.....| i,j | i+1,j |.....
| | |
| | |
_____|_______|_______|_____
| | |
| | |
.....| i,j+1 |i+1,j+1|.....
| | |
| | |
_____|_______|_______|_____
| | |
| | |
| | |
每个i,j
代表一个全屏。因此,设备的视口将只能(i,j)
在任何时间点查看特定内容。
从那个位置开始滑动屏幕
- 左,视口转到
i+1, j
- 对,视口转到
i-1, j
- 向上,视口转到
i, j+1
- 向下,视口转到
i, j-1
我想指定行数、列数。(不仅仅是4个)
到目前为止,这是我的代码。呈现 4 个这样的屏幕Video
(SVG)
(我还没有处理控制器逻辑)
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
// Hide the status bar
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Experiments',
theme: ThemeData.dark(),
home: MyHomePage(title: 'FlutterExps'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
children: [
PageView(
scrollDirection: Axis.vertical,
children: [
ColoredWidget(
color: Colors.cyan,
direction: ">",
),
ColoredWidget(
color: Colors.orange,
direction: ">>",
),
],
),
PageView(
scrollDirection: Axis.vertical,
children: [
ColoredWidget(
color: Colors.green,
direction: "<",
),
ColoredWidget(
color: Colors.yellow,
direction: "<<",
),
],
),
],
),
);
}
}
class ColoredWidget extends StatefulWidget {
final Color color;
final String direction;
const ColoredWidget({
Key key,
@required this.color,
@required this.direction,
}) : super(key: key);
@override
_ColoredWidgetState createState() => _ColoredWidgetState();
}
class _ColoredWidgetState extends State<ColoredWidget>
with AutomaticKeepAliveClientMixin<ColoredWidget> {
@override
Widget build(BuildContext context) {
super.build(context);
return Container(
color: widget.color,
child: Center(
child: Text(
widget.direction,
style: TextStyle(
fontSize: 100,
color: Colors.black,
),
),
));
}
@override
bool get wantKeepAlive => true;
}
但这显然行不通,因为我需要控制所有连接的相邻 PageViews 等。我不明白如何进行。
解决方案
我能够做到。
Github上的最小代码
// import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Experiments',
theme: ThemeData.dark(),
home: MyHomePage(title: 'FlutterExps'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<PageController> _controllers;
PageController _rowController;
// No need to use valuenotifiers here
// But I need pass by reference thus using it as a wrapper
ValueNotifier<int> currIdxNotifier = ValueNotifier(0);
ValueNotifier<int> currUpNotifier = ValueNotifier(0);
@override
void initState() {
_controllers = [
PageController(),
PageController(),
PageController(),
];
_rowController = PageController(
// initialPage: 0,
// keepPage: true,
);
currIdxNotifier.value = _rowController.initialPage;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
// pageSnapping: true,
controller: _rowController,
onPageChanged: (pno) {
// MUST set state and trigger a rebuild
// As horizontal viewport changed
setState(() {
currIdxNotifier.value = pno;
// print("${currIdxNotifier.value} horizz");
});
},
children: [
ColPageView(
idx: 0,
currup: currUpNotifier,
notifier: currIdxNotifier,
controllers: _controllers,
children: <Widget>[
ColoredWidget(
color: Colors.orange[50],
text: "0, 0",
),
ColoredWidget(
color: Colors.orange[100],
text: "0, 1",
),
ColoredWidget(
color: Colors.orange[200],
text: "0, 2",
),
ColoredWidget(
color: Colors.orange[300],
text: "0, 3",
),
],
),
ColPageView(
idx: 1,
currup: currUpNotifier,
notifier: currIdxNotifier,
controllers: _controllers,
children: [
ColoredWidget(
color: Colors.green[100],
text: "1, 0",
),
ColoredWidget(
color: Colors.green[200],
text: "1, 1",
),
ColoredWidget(
color: Colors.green[300],
text: "1, 2",
),
ColoredWidget(
color: Colors.green[400],
text: "1, 3",
),
],
),
ColPageView(
idx: 2,
currup: currUpNotifier,
notifier: currIdxNotifier,
controllers: _controllers,
children: [
ColoredWidget(
color: Colors.teal[100],
text: "2, 0",
),
ColoredWidget(
color: Colors.teal[200],
text: "2, 1",
),
ColoredWidget(
color: Colors.teal[300],
text: "2, 2",
),
ColoredWidget(
color: Colors.teal[400],
text: "2, 3",
),
],
),
],
),
);
}
}
/// All the vertical pageviews are here
class ColPageView extends StatefulWidget {
final List<Widget> children;
final List<PageController> controllers;
final ValueNotifier<int> notifier;
final ValueNotifier<int> currup;
final int idx;
const ColPageView({
Key key,
this.children = const <Widget>[],
@required this.idx,
@required this.currup,
@required this.notifier,
@required this.controllers,
}) : super(key: key);
@override
_ColPageViewState createState() => _ColPageViewState();
}
class _ColPageViewState extends State<ColPageView> {
@override
void initState() {
// Just initialized
// Set the start value to be the current vertical value
widget.controllers[widget.idx] = PageController(
initialPage: widget.currup.value ?? 0,
keepPage: true,
);
// print("INIT STATE ${widget.idx}");
// print("INIT STATE ${widget.currup.value}");
super.initState();
}
@override
Widget build(BuildContext context) {
return PageView(
// pageSnapping: true,
controller: widget.controllers[widget.idx],
// controller: widget.controller,
scrollDirection: Axis.vertical,
children: widget.children,
onPageChanged: (widget.notifier.value == widget.idx)
? (pno) {
// if the global horizontal page is the current widget
// var rand = Random();
// var randnn = rand.nextDouble();
widget.controllers.forEach((colpv) {
if (widget.controllers[widget.idx] == colpv) {
// print("same widget so return $randnn");
return;
}
// https://github.com/flutter/flutter/issues/20621#issuecomment-445504085
// Only if the controller has clients
bool isSelected = colpv.hasClients
? colpv.page == pno
: colpv.initialPage == pno;
// Not the same page as everyone
if (!isSelected) {
// print("not selected");
if (colpv.hasClients) {
colpv.animateToPage(
pno,
duration: Duration(milliseconds: 200),
curve: Curves.easeIn,
);
}
}
// set the current updated value of the vertical coord
widget.currup.value = pno;
// print("$pno $isSelected");
});
// print("col-${widget.idx} changed to $pno");
// set horizontal coord to be null
// As we've finished dealing with it
widget.notifier.value = null;
}
: (_) {
// Others which are not the currently moving pageview
// SHOULD not have any listeners
// Spent 5hrs trying to figure this out
// print("nope ${widget.notifier.value} == ${widget.idx}");
},
);
}
}
/// A Widget that simply displays a color and an input text
/// NOTE: This is a StatefulWidget because needs to use keepalive
class ColoredWidget extends StatefulWidget {
/// Color to display the widget in
final Color color;
final String text;
const ColoredWidget({
Key key,
@required this.color,
@required this.text,
}) : super(key: key);
@override
_ColoredWidgetState createState() => _ColoredWidgetState();
}
class _ColoredWidgetState extends State<ColoredWidget>
with AutomaticKeepAliveClientMixin<ColoredWidget> {
@override
Widget build(BuildContext context) {
super.build(context);
return Container(
color: widget.color,
child: Center(
child: Text(
widget.text,
style: TextStyle(
fontSize: 100,
color: Colors.black,
),
),
));
}
// Need to use this or the state of the pageview will be lost
// In this case, if not using keepalive it would still work but
// it will scroll down every time the page gets changed horizontally
// TODO: Destroy if not next to current pageview
@override
bool get wantKeepAlive => true;
}
关于如何链接到综合浏览量的类似问题(由我提出)。
推荐阅读
- php - php标头重定向忽略条件语句
- sql - Postgres:`timestamp without time zone at time zone`的含义
- flutter - Flutter App - Google Maps 打开时,计时器请求保持在队列中
- javascript - 这个 Typescript 代码中真正发生了什么。结果是硬编码字符串和参数化字符串错误
- c# - Blazor(服务器) - 重定向到取决于用户状态和页面属性的另一个页面
- kubernetes - 是否可以使用 Kubernetes 进行热重载?
- python - 熊猫:将多个文件加载到数据框中
- r - 使用混淆矩阵和插入符号统计的灵敏度和特异性的零 R 模型计算
- r - 根据 p 值有条件地将 geom_smooth 添加到 ggplot 构面
- r - InteractiveComplexHeatmap 将无法识别跨 k-means 集群的画笔输入