1.让界面之间可以嵌套且执行动画。
2.界面的添加遵循先进后出原则。
3.需要使用AnimateView,请看我上一篇博客。
演示:
代码:
Stack:
import 'package:flutter/cupertino.dart';
///栈,先进后出
class KqWidgetStack {
final List<Widget> _stack = [];
///入栈
push(Widget obj) {
_stack.add(obj);
}
///出栈
Widget? pop() {
if (_stack.isEmpty) {
return null;
} else {
return _stack.removeLast();
}
}
///栈长度
length() {
return _stack.length;
}
///清除栈
clear() {
_stack.clear();
}
}
StackView:
import 'package:flutter/cupertino.dart';
import 'package:kq_flutter_widgets/widgets/animate/animate_view.dart';
import 'package:kq_flutter_widgets/widgets/stackview/stack.dart';
class StackView extends StatefulWidget {
///初始显示的界面
final Widget initChild;
///state回调。获取state后方便后续界面操作。
final void Function(StackViewState state)? stateCallback;
const StackView({
super.key,
required this.initChild,
this.stateCallback,
});
@override
State<StatefulWidget> createState() => StackViewState();
}
class StackViewState extends State<StackView> {
final KqWidgetStack _stack = KqWidgetStack();
bool _isOpen = true;
Widget? _previousWidget;
Widget? _currentWidget;
@override
void initState() {
super.initState();
_currentWidget = widget.initChild;
widget.stateCallback?.call(this);
}
@override
Widget build(BuildContext context) {
if (_currentWidget == null) {
return Container();
} else if (_previousWidget == null) {
return _isOpen
? AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.bottomToTop.angle,
type: TranslationAnimateType.translateIn),
child: _currentWidget!,
)
: AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.topToBottom.angle,
type: TranslationAnimateType.translateOut),
child: _currentWidget!,
);
} else {
return _isOpen
? Stack(
children: [
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.bottomToTop.angle,
type: TranslationAnimateType.translateOut),
isNeedFlashEveryTime: true,
child: _previousWidget!,
),
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.bottomToTop.angle,
type: TranslationAnimateType.translateIn),
isNeedFlashEveryTime: true,
child: _currentWidget!,
),
],
)
: Stack(
children: [
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.topToBottom.angle,
type: TranslationAnimateType.translateOut),
isNeedFlashEveryTime: true,
child: _previousWidget!,
),
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.topToBottom.angle,
type: TranslationAnimateType.translateIn),
isNeedFlashEveryTime: true,
child: _currentWidget!,
),
],
);
}
}
addWidget(Widget page) {
_isOpen = true;
_previousWidget = _currentWidget;
_currentWidget = page;
if (_previousWidget != null) {
_stack.push(_previousWidget!);
}
print("stack size=${_stack.length()}");
setState(() {});
}
///回退,返回上一个界面。
///[bool] 返回true表示成功返回上一级,
///返回false表示返回失败,已是最后一个界面,不可继续返回。
bool back() {
_isOpen = false;
_previousWidget = _currentWidget;
_currentWidget = _stack.pop();
print("stack size=${_stack.length()}");
setState(() {});
if (_stack.length() > 1) {
return true;
} else {
return false;
}
}
@override
void dispose() {
super.dispose();
_stack.clear();
}
}
demo:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:kq_flutter_widgets/widgets/button/kq_small_button.dart';
import 'package:kq_flutter_widgets/widgets/stackview/stack_view.dart';
import 'package:kq_flutter_widgets/widgets/titleBar/kq_title_bar.dart';
class StackViewDemo extends StatefulWidget {
const StackViewDemo({super.key});
@override
State<StatefulWidget> createState() => StackViewDemoState();
}
class StackViewDemoState extends State<StackViewDemo> {
StackViewState? state;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: KqHeadBar(
headTitle: 'StackView演示',
back: () {
Get.back();
},
),
body: StackView(
initChild: Column(
children: [
const Text("我是首页"),
KqSmallButton(
title: "打开新页面",
onTap: (disabled) {
state?.addWidget(TestPage1(state: state!));
},
),
Expanded(child: Container(color: Colors.purple,)),
],
),
stateCallback: (StackViewState state) {
this.state = state;
},
),
);
}
}
class TestPage1 extends StatelessWidget {
final StackViewState state;
const TestPage1({super.key, required this.state});
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text("我是TestPage1"),
KqSmallButton(
title: "返回",
onTap: (disabled) {
state.back();
},
),
KqSmallButton(
title: "打开新页面",
onTap: (disabled) {
state.addWidget(TestPage2(state: state));
},
),
Expanded(child: Container(color: Colors.amber,)),
],
);
}
}
class TestPage2 extends StatelessWidget {
final StackViewState state;
const TestPage2({super.key, required this.state});
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text("我是TestPage2"),
KqSmallButton(
title: "返回",
onTap: (disabled) {
state.back();
},
),
KqSmallButton(
title: "打开新页面",
onTap: (disabled) {
state.addWidget(TestPage3(state: state));
},
),
Expanded(child: Container(color: Colors.cyan,)),
],
);
}
}
class TestPage3 extends StatelessWidget {
final StackViewState state;
const TestPage3({super.key, required this.state});
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text("我是TestPage3"),
KqSmallButton(
title: "返回",
onTap: (disabled) {
state.back();
},
),
Expanded(child: Container(color: Colors.blueAccent,)),
],
);
}
}
2023/9/1号更新:
由于只做了单纯的界面处理,没有做数据处理,后续加入了数据传递功能:
最新的StackView代码:
import 'package:flutter/cupertino.dart';
import 'package:kq_flutter_widgets/widgets/animate/animate_view.dart';
import 'package:kq_flutter_widgets/widgets/stackView/stack.dart';
class StackView extends StatefulWidget {
///初始显示的界面
final Widget? Function(StackViewState state) init;
const StackView({
super.key,
required this.init,
});
@override
State<StatefulWidget> createState() => StackViewState();
}
class StackViewState extends State<StackView> {
///栈
final KqWidgetStack _stack = KqWidgetStack();
///是否是打开的状态,true表示打开界面,false表示是关闭界面,默认为true
bool _isOpen = true;
///上一个界面
Widget? _previousWidget;
///当前界面
Widget? _currentWidget;
///参数
dynamic arguments;
///当当前界面为空时使用的零时变量
final Widget _tempNullWidget = Container();
///返回回调出缓存
final Map<Widget, Function(dynamic result)> _callbacks = {};
@override
void initState() {
super.initState();
_currentWidget = widget.init.call(this);
}
@override
Widget build(BuildContext context) {
if (_currentWidget == null) {
return Container();
} else if (_previousWidget == null) {
return _isOpen
? AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.bottomToTop.angle,
type: TranslationAnimateType.translateIn),
child: _currentWidget!,
)
: AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.topToBottom.angle,
type: TranslationAnimateType.translateOut),
child: _currentWidget!,
);
} else {
return _isOpen
? Stack(
children: [
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.bottomToTop.angle,
type: TranslationAnimateType.translateOut),
isNeedFlashEveryTime: true,
child: _previousWidget!,
),
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.bottomToTop.angle,
type: TranslationAnimateType.translateIn),
isNeedFlashEveryTime: true,
child: _currentWidget!,
),
],
)
: Stack(
children: [
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.topToBottom.angle,
type: TranslationAnimateType.translateOut),
isNeedFlashEveryTime: true,
child: _previousWidget!,
),
AnimateView(
animate: TranslationAnimate(
angle: TranslationAnimateDirection.topToBottom.angle,
type: TranslationAnimateType.translateIn),
isNeedFlashEveryTime: true,
child: _currentWidget!,
),
],
);
}
}
///以Map的方式获取参数,如果传入的参数不是Map形式,则需使用[arguments]获取参数
S? getArg<S>(String key) {
if (arguments == null) {
return null;
}
if (arguments is! Map) {
return null;
}
return arguments[key];
}
///添加一个widget,即打开一个界面。
///[page] 需要打开的界面。
///[arguments] 打开时需要给下一个界面传递的参数。
///[callback]打开的界面关闭时,回传的参数。
addWidget(Widget page,
{dynamic arguments, Function(dynamic result)? callback}) {
_isOpen = true;
_previousWidget = _currentWidget;
_currentWidget = page;
///传参赋值,取值时,直接使用[arguments]取值,或者使用[getArg]方法取值
this.arguments = arguments;
///存储回调,当初始界面为空时,使用临时key存储
if (callback != null) {
_callbacks.putIfAbsent(
_previousWidget ?? _tempNullWidget, () => callback);
}
if (_previousWidget != null) {
_stack.push(_previousWidget!);
}
setState(() {});
}
///回退,返回上一个界面。
///[bool] 返回true表示成功返回上一级,
///返回false表示返回失败,已是最后一个界面,不可继续返回。
bool back({dynamic result}) {
_isOpen = false;
_previousWidget = _currentWidget;
_currentWidget = _stack.pop();
///移除当前界面的callback,如果存在,调用call,并传递result的值
_callbacks.remove(_currentWidget)?.call(result);
setState(() {});
if (_stack.length() > 0) {
return true;
} else {
return false;
}
}
///是否可返回
bool backAble() {
return _stack.length() > 0;
}
/// 关闭
close({dynamic result}) {
_callbacks[_tempNullWidget]?.call(result);
_stack.clear();
_callbacks.clear();
_currentWidget = null;
setState(() {});
}
@override
void dispose() {
super.dispose();
_stack.clear();
_callbacks.clear();
}
}