mod: 视频详情、跳转Hero效果
This commit is contained in:
@ -47,7 +47,6 @@ class HomeController extends GetxController {
|
||||
|
||||
// 上拉加载
|
||||
Future onLoad() async {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
queryRcmdFeed('onLoad');
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ class VideoDetailController extends GetxController {
|
||||
// 请求状态
|
||||
RxBool isLoading = false.obs;
|
||||
|
||||
String heroTag = '';
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@ -24,6 +25,7 @@ class VideoDetailController extends GetxController {
|
||||
videoItem['pic'] = args.pic;
|
||||
}
|
||||
}
|
||||
heroTag = Get.arguments['heroTag'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
lib/pages/video/detail/introduction/controller.dart
Normal file
47
lib/pages/video/detail/introduction/controller.dart
Normal file
@ -0,0 +1,47 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/api.dart';
|
||||
import 'package:pilipala/http/init.dart';
|
||||
import 'package:pilipala/models/video_detail_res.dart';
|
||||
|
||||
class VideoIntroController extends GetxController {
|
||||
// 视频aid
|
||||
String aid = Get.parameters['aid']!;
|
||||
|
||||
// 是否预渲染 骨架屏
|
||||
bool preRender = false;
|
||||
|
||||
// 视频详情 上个页面传入
|
||||
Map? videoItem = {};
|
||||
|
||||
// 请求状态
|
||||
RxBool isLoading = false.obs;
|
||||
|
||||
// 视频详情 请求返回
|
||||
Rx<VideoDetailData> videoDetail = VideoDetailData().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
if (Get.arguments.isNotEmpty) {
|
||||
if (Get.arguments.containsKey('videoItem')) {
|
||||
preRender = true;
|
||||
var args = Get.arguments['videoItem'];
|
||||
videoItem!['pic'] = args.pic;
|
||||
videoItem!['title'] = args.title;
|
||||
videoItem!['stat'] = args.stat;
|
||||
videoItem!['pubdate'] = args.pubdate;
|
||||
videoItem!['owner'] = args.owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future queryVideoDetail() async {
|
||||
var res = await Request().get(Api.videoDetail, data: {
|
||||
'aid': aid,
|
||||
});
|
||||
VideoDetailResponse result = VideoDetailResponse.fromJson(res.data);
|
||||
videoDetail.value = result.data!;
|
||||
// await Future.delayed(const Duration(seconds: 3));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
4
lib/pages/video/detail/introduction/index.dart
Normal file
4
lib/pages/video/detail/introduction/index.dart
Normal file
@ -0,0 +1,4 @@
|
||||
library video_detail_introduction;
|
||||
|
||||
export './controller.dart';
|
||||
export './view.dart';
|
||||
471
lib/pages/video/detail/introduction/view.dart
Normal file
471
lib/pages/video/detail/introduction/view.dart
Normal file
@ -0,0 +1,471 @@
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/pages/video/detail/widgets/expandable_section.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||
import 'package:pilipala/models/video_detail_res.dart';
|
||||
import 'package:pilipala/pages/video/detail/introduction/controller.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class VideoIntroPanel extends StatefulWidget {
|
||||
const VideoIntroPanel({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<VideoIntroPanel> createState() => _VideoIntroPanelState();
|
||||
}
|
||||
|
||||
class _VideoIntroPanelState extends State<VideoIntroPanel> {
|
||||
final VideoIntroController videoIntroController =
|
||||
Get.put(VideoIntroController());
|
||||
VideoDetailData? videoDetail;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
videoIntroController.videoDetail.listen((value) {
|
||||
videoDetail = value;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
videoIntroController.onClose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: videoIntroController.queryVideoDetail(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
print(snapshot.data);
|
||||
if (snapshot.data) {
|
||||
// 请求成功
|
||||
return _buildView(context, false, videoDetail);
|
||||
} else {
|
||||
// 请求错误
|
||||
return Center(
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return _buildView(context, true, videoDetail);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildView(context, loadingStatus, videoDetail) {
|
||||
// return CustomScrollView(
|
||||
// key: const PageStorageKey<String>('简介'),
|
||||
// slivers: <Widget>[
|
||||
// SliverOverlapInjector(
|
||||
// handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)),
|
||||
// VideoInfo(loadingStatus: loadingStatus, videoDetail: videoDetail),
|
||||
// SliverToBoxAdapter(
|
||||
// child:
|
||||
// Divider(color: Theme.of(context).dividerColor.withOpacity(0.1)),
|
||||
// ),
|
||||
// const RecommendList()
|
||||
// ],
|
||||
// );
|
||||
return VideoInfo(loadingStatus: loadingStatus, videoDetail: videoDetail);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoInfo extends StatefulWidget {
|
||||
bool loadingStatus = false;
|
||||
VideoDetailData? videoDetail;
|
||||
|
||||
VideoInfo({Key? key, required this.loadingStatus, this.videoDetail})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<VideoInfo> createState() => _VideoInfoState();
|
||||
}
|
||||
|
||||
class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
Map videoItem = Get.put(VideoIntroController()).videoItem!;
|
||||
bool isExpand = false;
|
||||
|
||||
/// 手动控制动画的控制器
|
||||
late AnimationController? _manualController;
|
||||
|
||||
/// 手动控制
|
||||
late Animation<double>? _manualAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
/// 不设置重复,使用代码控制进度,动画时间1秒
|
||||
_manualController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 400),
|
||||
);
|
||||
_manualAnimation =
|
||||
Tween<double>(begin: 0.5, end: 1.5).animate(_manualController!);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 25),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: !widget.loadingStatus || videoItem.isNotEmpty
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
src: !widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.face
|
||||
: videoItem['owner'].face,
|
||||
width: 38,
|
||||
height: 38,
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(!widget.loadingStatus
|
||||
? widget.videoDetail!.owner!.name
|
||||
: videoItem['owner'].name),
|
||||
const SizedBox(height: 2),
|
||||
// Text.rich(
|
||||
// TextSpan(
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .outline,
|
||||
// fontSize: 11),
|
||||
// children: const [
|
||||
// TextSpan(text: '2.6万粉丝'),
|
||||
// TextSpan(text: ' '),
|
||||
// TextSpan(text: '2.6万粉丝'),
|
||||
// ]),
|
||||
// ),
|
||||
]),
|
||||
const Spacer(),
|
||||
AnimatedOpacity(
|
||||
opacity: widget.loadingStatus ? 0 : 1,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {}, child: const Text('+ 关注')),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
// 标题 超过两行收起
|
||||
// Container(
|
||||
// color: Colors.blue[50],
|
||||
// child: SizedOverflowBox(
|
||||
// size: const Size(50.0, 50.0),
|
||||
// alignment: AlignmentDirectional.bottomStart,
|
||||
// child: Container(height: 150.0, width: 150.0, color: Colors.blue,),
|
||||
// ),
|
||||
// ),
|
||||
// Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: ExpandedSection(
|
||||
// expand: false,
|
||||
// begin: 1,
|
||||
// end: 1,
|
||||
// child: Text(
|
||||
// !widget.loadingStatus
|
||||
// ? widget.videoDetail!.title
|
||||
// : videoItem['title'],
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// maxLines: 1,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 10),
|
||||
// RotationTransition(
|
||||
// turns: _manualAnimation!,
|
||||
// child: IconButton(
|
||||
// onPressed: () {
|
||||
// /// 获取动画当前的值
|
||||
// var value = _manualController!.value;
|
||||
|
||||
// /// 0.5代表 180弧度
|
||||
// if (value == 0) {
|
||||
// _manualController!.animateTo(0.5);
|
||||
// } else {
|
||||
// _manualController!.animateTo(0);
|
||||
// }
|
||||
// setState(() {
|
||||
// isExpand = !isExpand;
|
||||
// });
|
||||
// },
|
||||
// icon: const Icon(Icons.expand_less)),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
!widget.loadingStatus
|
||||
? widget.videoDetail!.title
|
||||
: videoItem['title'],
|
||||
// style: Theme.of(context).textTheme.titleMedium,
|
||||
// maxLines: 2,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 5),
|
||||
// 播放量、评论、日期
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 2),
|
||||
StatView(
|
||||
theme: 'gray',
|
||||
view: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.view
|
||||
: videoItem['stat'].view,
|
||||
size: 'medium',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
StatDanMu(
|
||||
theme: 'gray',
|
||||
danmu: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.danmaku
|
||||
: videoItem['stat'].danmaku,
|
||||
size: 'medium',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
Utils.dateFormat(
|
||||
!widget.loadingStatus
|
||||
? widget.videoDetail!.pubdate
|
||||
: videoItem['pubdate'],
|
||||
formatType: 'detail'),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
const Spacer(),
|
||||
RotationTransition(
|
||||
turns: _manualAnimation!,
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
/// 获取动画当前的值
|
||||
var value = _manualController!.value;
|
||||
|
||||
/// 0.5代表 180弧度
|
||||
if (value == 0) {
|
||||
_manualController!.animateTo(0.5);
|
||||
} else {
|
||||
_manualController!.animateTo(0);
|
||||
}
|
||||
setState(() {
|
||||
isExpand = !isExpand;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.expand_less,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// const SizedBox(height: 5),
|
||||
// 简介 默认收起
|
||||
if (!widget.loadingStatus)
|
||||
ExpandedSection(
|
||||
expand: isExpand,
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
child: DefaultTextStyle(
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
height: 1.5,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelMedium?.fontSize,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: SelectableRegion(
|
||||
magnifierConfiguration:
|
||||
const TextMagnifierConfiguration(),
|
||||
focusNode: FocusNode(),
|
||||
selectionControls: MaterialTextSelectionControls(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(widget.videoDetail!.bvid!),
|
||||
Text(widget.videoDetail!.desc!),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
_actionGrid(context),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
)
|
||||
: const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 喜欢 投币 分享
|
||||
Widget _actionGrid(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return Container(
|
||||
color: Colors.black12,
|
||||
height: constraints.maxWidth / 5,
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
crossAxisCount: 5,
|
||||
children: <Widget>[
|
||||
ActionItem(
|
||||
icon: const Icon(Icons.thumb_up),
|
||||
onTap: () => {},
|
||||
selectStatus: false,
|
||||
loadingStatus: widget.loadingStatus,
|
||||
text: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.like!.toString()
|
||||
: '-'),
|
||||
ActionItem(
|
||||
icon: const Icon(Icons.thumb_down),
|
||||
onTap: () => {},
|
||||
selectStatus: false,
|
||||
loadingStatus: widget.loadingStatus,
|
||||
text: '不喜欢'),
|
||||
ActionItem(
|
||||
icon: const Icon(Icons.generating_tokens),
|
||||
onTap: () => {},
|
||||
selectStatus: false,
|
||||
loadingStatus: widget.loadingStatus,
|
||||
text: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.coin!.toString()
|
||||
: '-'),
|
||||
ActionItem(
|
||||
icon: const Icon(Icons.star),
|
||||
onTap: () => {},
|
||||
selectStatus: false,
|
||||
loadingStatus: widget.loadingStatus,
|
||||
text: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.favorite!.toString()
|
||||
: '-'),
|
||||
ActionItem(
|
||||
icon: const Icon(Icons.share),
|
||||
onTap: () => {},
|
||||
selectStatus: false,
|
||||
loadingStatus: widget.loadingStatus,
|
||||
text: !widget.loadingStatus
|
||||
? widget.videoDetail!.stat!.share!.toString()
|
||||
: '-'),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ActionItem extends StatelessWidget {
|
||||
Icon? icon;
|
||||
Function? onTap;
|
||||
bool? loadingStatus;
|
||||
String? text;
|
||||
bool selectStatus = false;
|
||||
|
||||
ActionItem({
|
||||
Key? key,
|
||||
this.icon,
|
||||
this.onTap,
|
||||
this.loadingStatus,
|
||||
this.text,
|
||||
required this.selectStatus,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
child: Ink(
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon!.icon!,
|
||||
color: selectStatus
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context).colorScheme.outline),
|
||||
const SizedBox(height: 2),
|
||||
AnimatedOpacity(
|
||||
opacity: loadingStatus! ? 0 : 1,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Text(
|
||||
text!,
|
||||
style: TextStyle(
|
||||
color: selectStatus
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
fontSize: Theme.of(context).textTheme.labelSmall?.fontSize),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class RecommendList extends StatelessWidget {
|
||||
const RecommendList({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return Material(
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
|
||||
child: Text(
|
||||
'$index」 求推荐一些高质量的系统地介绍 ChatGPT 及相关技术的视频、文章或者书',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(height: 1.6),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, childCount: 50),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ActionGrid extends StatelessWidget {
|
||||
const ActionGrid({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/pages/video/detail/controller.dart';
|
||||
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
||||
|
||||
class VideoDetailPage extends StatefulWidget {
|
||||
const VideoDetailPage({Key? key}) : super(key: key);
|
||||
@ -52,10 +53,13 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
||||
double maxWidth = boxConstraints.maxWidth;
|
||||
double maxHeight = boxConstraints.maxHeight;
|
||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
||||
return NetworkImgLayer(
|
||||
src: videoDetailController.videoItem['pic'],
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
return Hero(
|
||||
tag: videoDetailController.heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: videoDetailController.videoItem['pic'],
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -112,10 +116,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
|
||||
context),
|
||||
),
|
||||
// const VideoIntroPanel(),
|
||||
const SliverToBoxAdapter(
|
||||
child: Text('简介'),
|
||||
)
|
||||
const VideoIntroPanel(),
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
||||
83
lib/pages/video/detail/widgets/expandable_section.dart
Normal file
83
lib/pages/video/detail/widgets/expandable_section.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ExpandedSection extends StatefulWidget {
|
||||
final Widget child;
|
||||
final bool expand;
|
||||
double begin = 0.0;
|
||||
double end = 1.0;
|
||||
|
||||
ExpandedSection(
|
||||
{this.expand = false,
|
||||
required this.child,
|
||||
required this.begin,
|
||||
required this.end});
|
||||
|
||||
@override
|
||||
_ExpandedSectionState createState() => _ExpandedSectionState();
|
||||
}
|
||||
|
||||
class _ExpandedSectionState extends State<ExpandedSection>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController expandController;
|
||||
late Animation<double> animation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
prepareAnimations();
|
||||
_runExpandCheck();
|
||||
}
|
||||
|
||||
///Setting up the animation
|
||||
// void prepareAnimations() {
|
||||
// expandController = AnimationController(
|
||||
// vsync: this, duration: const Duration(milliseconds: 500));
|
||||
// animation = CurvedAnimation(
|
||||
// parent: expandController,
|
||||
// curve: Curves.fastOutSlowIn,
|
||||
// );
|
||||
// }
|
||||
|
||||
void prepareAnimations() {
|
||||
expandController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 400));
|
||||
Animation<double> curve = CurvedAnimation(
|
||||
parent: expandController,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
animation = Tween(begin: widget.begin, end: widget.end).animate(curve);
|
||||
// animation = CurvedAnimation(
|
||||
// parent: expandController,
|
||||
// curve: Curves.fastOutSlowIn,
|
||||
// );
|
||||
}
|
||||
|
||||
void _runExpandCheck() {
|
||||
if (widget.expand) {
|
||||
expandController.forward();
|
||||
} else {
|
||||
expandController.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(ExpandedSection oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_runExpandCheck();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
expandController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizeTransition(
|
||||
axisAlignment: -1.0,
|
||||
sizeFactor: animation,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user