import 'package:flutter/material.dart'; import 'dart:async'; import 'package:shuqi/public.dart'; class ReaderMenu extends StatefulWidget { final List chapters; final int articleIndex; final VoidCallback onTap; final VoidCallback onPreviousArticle; final VoidCallback onNextArticle; final void Function(Chapter chapter) onToggleChapter; ReaderMenu({required this.chapters, required this.articleIndex, required this.onTap, required this.onPreviousArticle, required this.onNextArticle, required this.onToggleChapter}); @override _ReaderMenuState createState() => _ReaderMenuState(); } class _ReaderMenuState extends State with SingleTickerProviderStateMixin { late AnimationController animationController; late Animation animation; late double progressValue; bool isTipVisible = false; @override initState() { super.initState(); progressValue = this.widget.articleIndex / (this.widget.chapters.length - 1); animationController = AnimationController(duration: const Duration(milliseconds: 200), vsync: this); animation = Tween(begin: 0.0, end: 1.0).animate(animationController); animation.addListener(() { setState(() {}); }); animationController.forward(); } @override void didUpdateWidget(ReaderMenu oldWidget) { super.didUpdateWidget(oldWidget); progressValue = this.widget.articleIndex / (this.widget.chapters.length - 1); } @override void dispose() { animationController.dispose(); super.dispose(); } hide() { animationController.reverse(); Timer(Duration(milliseconds: 200), () { this.widget.onTap(); }); setState(() { isTipVisible = false; }); } buildTopView(BuildContext context) { return Positioned( top: -Screen.navigationBarHeight * (1 - animation.value), left: 0, right: 0, child: Container( decoration: BoxDecoration(color: SQColor.paper, boxShadow: Styles.borderShadow), height: Screen.navigationBarHeight, padding: EdgeInsets.fromLTRB(5, Screen.topSafeHeight, 5, 0), child: Row( children: [ Container( width: 44, child: GestureDetector( onTap: () { Navigator.pop(context); }, child: Image.asset('assets/img/pub_back_gray.png'), ), ), Expanded(child: Container()), Container( width: 44, child: Image.asset('assets/img/read_icon_voice.png'), ), Container( width: 44, child: Image.asset('assets/img/read_icon_more.png'), ), ], ), ), ); } int currentArticleIndex() { return ((this.widget.chapters.length - 1) * progressValue).toInt(); } buildProgressTipView() { if (!isTipVisible) { return Container(); } Chapter chapter = this.widget.chapters[currentArticleIndex()]; double percentage = chapter.index / (this.widget.chapters.length - 1) * 100; return Container( decoration: BoxDecoration(color: Color(0xff00C88D), borderRadius: BorderRadius.circular(5)), margin: EdgeInsets.fromLTRB(15, 0, 15, 10), padding: EdgeInsets.all(15), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(chapter.title, style: TextStyle(color: Colors.white, fontSize: 16)), Text('${percentage.toStringAsFixed(1)}%', style: TextStyle(color: SQColor.lightGray, fontSize: 12)), ], ), ); } previousArticle() { if (this.widget.articleIndex == 0) { Toast.show('已经是第一章了'); return; } this.widget.onPreviousArticle(); setState(() { isTipVisible = true; }); } nextArticle() { if (this.widget.articleIndex == this.widget.chapters.length - 1) { Toast.show('已经是最后一章了'); return; } this.widget.onNextArticle(); setState(() { isTipVisible = true; }); } buildProgressView() { return Container( padding: EdgeInsets.fromLTRB(5, 0, 5, 0), child: Row( children: [ GestureDetector( onTap: previousArticle, child: Container( padding: EdgeInsets.all(20), child: Image.asset('assets/img/read_icon_chapter_previous.png'), ), ), Expanded( child: Slider( value: progressValue, onChanged: (double value) { setState(() { isTipVisible = true; progressValue = value; }); }, onChangeEnd: (double value) { Chapter chapter = this.widget.chapters[currentArticleIndex()]; this.widget.onToggleChapter(chapter); }, activeColor: SQColor.primary, inactiveColor: SQColor.gray, ), ), GestureDetector( onTap: nextArticle, child: Container( padding: EdgeInsets.all(20), child: Image.asset('assets/img/read_icon_chapter_next.png'), ), ) ], ), ); } buildBottomView() { return Positioned( bottom: -(Screen.bottomSafeHeight + 110) * (1 - animation.value), left: 0, right: 0, child: Column( children: [ buildProgressTipView(), Container( decoration: BoxDecoration(color: SQColor.paper, boxShadow: Styles.borderShadow), padding: EdgeInsets.only(bottom: Screen.bottomSafeHeight), child: Column( children: [ buildProgressView(), buildBottomMenus(), ], ), ) ], ), ); } buildBottomMenus() { return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ buildBottomItem('目录', 'assets/img/read_icon_catalog.png'), buildBottomItem('亮度', 'assets/img/read_icon_brightness.png'), buildBottomItem('字体', 'assets/img/read_icon_font.png'), buildBottomItem('设置', 'assets/img/read_icon_setting.png'), ], ); } buildBottomItem(String title, String icon) { return Container( padding: EdgeInsets.symmetric(vertical: 7), child: Column( children: [ Image.asset(icon), SizedBox(height: 5), Text(title, style: TextStyle(fontSize: fixedFontSize(12), color: SQColor.darkGray)), ], ), ); } @override Widget build(BuildContext context) { return Container( child: Stack( children: [ GestureDetector( onTapDown: (_) { hide(); }, child: Container(color: Colors.transparent), ), buildTopView(context), buildBottomView(), ], ), ); } }