Showing
19 changed files
with
215 additions
and
847 deletions
... | @@ -3,6 +3,7 @@ import 'package:Parlando/account/view_models/account_view_model.dart'; | ... | @@ -3,6 +3,7 @@ import 'package:Parlando/account/view_models/account_view_model.dart'; |
3 | import 'package:Parlando/apis/api_response.dart'; | 3 | import 'package:Parlando/apis/api_response.dart'; |
4 | import 'package:Parlando/login/login_router.dart'; | 4 | import 'package:Parlando/login/login_router.dart'; |
5 | import 'package:Parlando/res/constant.dart'; | 5 | import 'package:Parlando/res/constant.dart'; |
6 | +import 'package:Parlando/routers/routers.dart'; | ||
6 | import 'package:cached_network_image/cached_network_image.dart'; | 7 | import 'package:cached_network_image/cached_network_image.dart'; |
7 | import 'package:flustars/flustars.dart'; | 8 | import 'package:flustars/flustars.dart'; |
8 | import 'package:flutter/material.dart'; | 9 | import 'package:flutter/material.dart'; |
... | @@ -121,6 +122,8 @@ class _AccountPageState extends State<AccountPage> { | ... | @@ -121,6 +122,8 @@ class _AccountPageState extends State<AccountPage> { |
121 | likeButton, | 122 | likeButton, |
122 | avatar, | 123 | avatar, |
123 | Positioned( | 124 | Positioned( |
125 | + left: 64.px, | ||
126 | + bottom: 10.px, | ||
124 | child: GestureDetector( | 127 | child: GestureDetector( |
125 | child: CircleAvatar( | 128 | child: CircleAvatar( |
126 | backgroundColor: Colors.red.withOpacity(0.4), | 129 | backgroundColor: Colors.red.withOpacity(0.4), |
... | @@ -136,8 +139,6 @@ class _AccountPageState extends State<AccountPage> { | ... | @@ -136,8 +139,6 @@ class _AccountPageState extends State<AccountPage> { |
136 | context, AccountRouter.accountEditPage); | 139 | context, AccountRouter.accountEditPage); |
137 | }, | 140 | }, |
138 | ), | 141 | ), |
139 | - left: 64.px, | ||
140 | - bottom: 10.px, | ||
141 | ), | 142 | ), |
142 | ], | 143 | ], |
143 | ), | 144 | ), |
... | @@ -249,6 +250,51 @@ class _AccountPageState extends State<AccountPage> { | ... | @@ -249,6 +250,51 @@ class _AccountPageState extends State<AccountPage> { |
249 | ], | 250 | ], |
250 | ), | 251 | ), |
251 | ), | 252 | ), |
253 | + bottomNavigationBar: Consumer( | ||
254 | + builder: (_, provider, __) { | ||
255 | + return BottomAppBar( | ||
256 | + color: Colors.grey, | ||
257 | + child: Row( | ||
258 | + mainAxisSize: MainAxisSize.max, | ||
259 | + mainAxisAlignment: MainAxisAlignment.spaceAround, | ||
260 | + children: <Widget>[ | ||
261 | + InkWell( | ||
262 | + onTap: () { | ||
263 | + NavigatorUtils.push(context, Routes.home, | ||
264 | + clearStack: true); | ||
265 | + }, | ||
266 | + child: Container( | ||
267 | + alignment: Alignment.center, | ||
268 | + height: 36.0, | ||
269 | + child: Text( | ||
270 | + ParlandoLocalizations.of(context) | ||
271 | + .onePoemBottomNavigationBarItemTitle, | ||
272 | + style: const TextStyle( | ||
273 | + color: Colors.white54, | ||
274 | + fontSize: 15.0, | ||
275 | + ), | ||
276 | + ), | ||
277 | + ), | ||
278 | + ), | ||
279 | + InkWell( | ||
280 | + onTap: () {}, | ||
281 | + child: Container( | ||
282 | + alignment: Alignment.center, | ||
283 | + height: 36.0, | ||
284 | + child: Text( | ||
285 | + ParlandoLocalizations.of(context) | ||
286 | + .profileBottomNavigationBarItemTitle, | ||
287 | + style: const TextStyle( | ||
288 | + color: Colors.white54, | ||
289 | + fontSize: 15.0, | ||
290 | + ), | ||
291 | + ), | ||
292 | + ), | ||
293 | + ), | ||
294 | + ]), | ||
295 | + ); | ||
296 | + }, | ||
297 | + ), | ||
252 | ); | 298 | ); |
253 | case Status.ERROR: | 299 | case Status.ERROR: |
254 | return Center( | 300 | return Center( | ... | ... |
... | @@ -41,7 +41,7 @@ class _PermissionRequestWidgetState extends State<PermissionRequestWidget> | ... | @@ -41,7 +41,7 @@ class _PermissionRequestWidgetState extends State<PermissionRequestWidget> |
41 | checkPermission(); | 41 | checkPermission(); |
42 | //注册观察者 | 42 | //注册观察者 |
43 | if (WidgetsBinding.instance != null) { | 43 | if (WidgetsBinding.instance != null) { |
44 | - WidgetsBinding.instance!.addObserver(this); | 44 | + WidgetsBinding.instance.addObserver(this); |
45 | } | 45 | } |
46 | } | 46 | } |
47 | 47 | ||
... | @@ -164,7 +164,7 @@ class _PermissionRequestWidgetState extends State<PermissionRequestWidget> | ... | @@ -164,7 +164,7 @@ class _PermissionRequestWidgetState extends State<PermissionRequestWidget> |
164 | void dispose() { | 164 | void dispose() { |
165 | //注销观察者 | 165 | //注销观察者 |
166 | if (WidgetsBinding.instance != null) { | 166 | if (WidgetsBinding.instance != null) { |
167 | - WidgetsBinding.instance!.removeObserver(this); | 167 | + WidgetsBinding.instance.removeObserver(this); |
168 | } | 168 | } |
169 | 169 | ||
170 | super.dispose(); | 170 | super.dispose(); | ... | ... |
1 | -import 'package:Parlando/events/trans_event.dart'; | 1 | +import 'package:Parlando/common/permission_request_widget.dart'; |
2 | -import 'package:Parlando/poem/poem_router.dart'; | ||
3 | import 'package:Parlando/routers/fluro_navigator.dart'; | 2 | import 'package:Parlando/routers/fluro_navigator.dart'; |
4 | -import 'package:Parlando/widgets/radial/flutter_radial_menu.dart'; | ||
5 | import 'package:flutter/material.dart'; | 3 | import 'package:flutter/material.dart'; |
6 | import 'package:Parlando/account/page/account_page.dart'; | 4 | import 'package:Parlando/account/page/account_page.dart'; |
7 | import 'package:Parlando/poem/page/poem_page.dart'; | 5 | import 'package:Parlando/poem/page/poem_page.dart'; |
8 | import 'package:Parlando/widgets/double_tap_back_exit_app.dart'; | 6 | import 'package:Parlando/widgets/double_tap_back_exit_app.dart'; |
9 | import 'package:provider/provider.dart'; | 7 | import 'package:provider/provider.dart'; |
10 | -import 'package:flutter_gen/gen_l10n/Parlando_localizations.dart'; | ||
11 | import 'provider/home_provider.dart'; | 8 | import 'provider/home_provider.dart'; |
9 | +import 'package:permission_handler/permission_handler.dart'; | ||
12 | 10 | ||
13 | class Home extends StatefulWidget { | 11 | class Home extends StatefulWidget { |
14 | const Home({Key? key}) : super(key: key); | 12 | const Home({Key? key}) : super(key: key); |
... | @@ -17,56 +15,42 @@ class Home extends StatefulWidget { | ... | @@ -17,56 +15,42 @@ class Home extends StatefulWidget { |
17 | _HomeState createState() => _HomeState(); | 15 | _HomeState createState() => _HomeState(); |
18 | } | 16 | } |
19 | 17 | ||
20 | -enum MenuOptions { | ||
21 | - audio, | ||
22 | - video, | ||
23 | -} | ||
24 | - | ||
25 | class _HomeState extends State<Home> with RestorationMixin { | 18 | class _HomeState extends State<Home> with RestorationMixin { |
26 | late List<Widget> _pageList; | 19 | late List<Widget> _pageList; |
27 | final PageController _pageController = PageController(); | 20 | final PageController _pageController = PageController(); |
28 | 21 | ||
29 | HomeProvider provider = HomeProvider(); | 22 | HomeProvider provider = HomeProvider(); |
30 | - final GlobalKey<RadialMenuState> _menuKey = GlobalKey<RadialMenuState>(); | ||
31 | - final List<RadialMenuItem<MenuOptions>> items = <RadialMenuItem<MenuOptions>>[ | ||
32 | - const RadialMenuItem<MenuOptions>( | ||
33 | - tooltip: 'audio', | ||
34 | - value: MenuOptions.audio, | ||
35 | - child: Icon( | ||
36 | - Icons.mic_none_outlined, | ||
37 | - ), | ||
38 | - iconColor: Colors.white, | ||
39 | - backgroundColor: Colors.blue, | ||
40 | - ), | ||
41 | - const RadialMenuItem<MenuOptions>( | ||
42 | - tooltip: "video", | ||
43 | - value: MenuOptions.video, | ||
44 | - child: Icon( | ||
45 | - Icons.video_call_outlined, | ||
46 | - ), | ||
47 | - iconColor: Colors.white, | ||
48 | - backgroundColor: Colors.green, | ||
49 | - ), | ||
50 | - ]; | ||
51 | - | ||
52 | - void _onItemSelected(MenuOptions value) { | ||
53 | - if (value == MenuOptions.video) { | ||
54 | - NavigatorUtils.push( | ||
55 | - context, | ||
56 | - '${PoemRouter.poemRecordAudioPage}?id=100', | ||
57 | - ); | ||
58 | - } else if (value == MenuOptions.audio) { | ||
59 | - NavigatorUtils.push( | ||
60 | - context, | ||
61 | - '${PoemRouter.poemRecordVideoPage}?data=100', | ||
62 | - ); | ||
63 | - } | ||
64 | - } | ||
65 | 23 | ||
66 | @override | 24 | @override |
67 | void initState() { | 25 | void initState() { |
68 | super.initState(); | 26 | super.initState(); |
69 | initData(); | 27 | initData(); |
28 | + List<String> list = [ | ||
29 | + "为您更好的体验应用,所以需要获取您的手机摄像头权限,以保存您的一些偏好设置", | ||
30 | + "您已拒绝权限,所以无法保存您的一些偏好设置,将无法使用APP", | ||
31 | + "您已拒绝权限,请在设置中心中同意APP的权限请求", | ||
32 | + "其他错误" | ||
33 | + ]; | ||
34 | + | ||
35 | + Future.delayed( | ||
36 | + Duration.zero, | ||
37 | + () { | ||
38 | + NavigatorUtils.pushPageByFade( | ||
39 | + context: context, | ||
40 | + //目标页面 | ||
41 | + targetPage: PermissionRequestWidget( | ||
42 | + //所需要申请的权限 | ||
43 | + permission: Permission.camera, | ||
44 | + //显示关闭应用按钮 | ||
45 | + isCloseApp: true, | ||
46 | + //提示文案 | ||
47 | + permissionList: list, | ||
48 | + ), | ||
49 | + //权限申请结果 | ||
50 | + dismissCallBack: (value) {}, | ||
51 | + ); | ||
52 | + }, | ||
53 | + ); | ||
70 | } | 54 | } |
71 | 55 | ||
72 | @override | 56 | @override |
... | @@ -88,87 +72,6 @@ class _HomeState extends State<Home> with RestorationMixin { | ... | @@ -88,87 +72,6 @@ class _HomeState extends State<Home> with RestorationMixin { |
88 | create: (_) => provider, | 72 | create: (_) => provider, |
89 | child: DoubleTapBackExitApp( | 73 | child: DoubleTapBackExitApp( |
90 | child: Scaffold( | 74 | child: Scaffold( |
91 | - floatingActionButton: FloatingActionButton( | ||
92 | - onPressed: () { | ||
93 | - eventBus.fire(TransEvent()); | ||
94 | - NavigatorUtils.push( | ||
95 | - context, | ||
96 | - '${PoemRouter.poemRecordVideoPage}?data=100', | ||
97 | - ); | ||
98 | - }, | ||
99 | - tooltip: "发一言", | ||
100 | - backgroundColor: Colors.white, | ||
101 | - child: const Icon( | ||
102 | - Icons.video_call_outlined, | ||
103 | - color: Colors.black45, | ||
104 | - ), | ||
105 | - ), | ||
106 | - // floatingActionButton: SizedBox( | ||
107 | - // height: 60, | ||
108 | - // child: RadialMenu( | ||
109 | - // key: _menuKey, | ||
110 | - // items: items, | ||
111 | - // radius: 80.0, | ||
112 | - // onSelected: _onItemSelected, | ||
113 | - // progressAnimationDuration:const Duration(milliseconds: 1), | ||
114 | - // ), | ||
115 | - // ), | ||
116 | - floatingActionButtonLocation: | ||
117 | - FloatingActionButtonLocation.centerDocked, | ||
118 | - bottomNavigationBar: Consumer<HomeProvider>( | ||
119 | - builder: (_, provider, __) { | ||
120 | - return BottomAppBar( | ||
121 | - color: Colors.black45, | ||
122 | - child: Row( | ||
123 | - mainAxisSize: MainAxisSize.max, | ||
124 | - mainAxisAlignment: MainAxisAlignment.spaceAround, | ||
125 | - children: <Widget>[ | ||
126 | - InkWell( | ||
127 | - onTap: () { | ||
128 | - _pageController.animateToPage( | ||
129 | - 0, | ||
130 | - duration: const Duration(milliseconds: 100), | ||
131 | - curve: Curves.easeOutSine, | ||
132 | - ); | ||
133 | - }, | ||
134 | - child: Container( | ||
135 | - alignment: Alignment.center, | ||
136 | - height: 36.0, | ||
137 | - child: Text( | ||
138 | - ParlandoLocalizations.of(context) | ||
139 | - .onePoemBottomNavigationBarItemTitle, | ||
140 | - style: const TextStyle( | ||
141 | - color: Colors.white54, | ||
142 | - fontSize: 15.0, | ||
143 | - ), | ||
144 | - ), | ||
145 | - ), | ||
146 | - ), | ||
147 | - InkWell( | ||
148 | - onTap: () { | ||
149 | - _pageController.animateToPage( | ||
150 | - 1, | ||
151 | - duration: const Duration(milliseconds: 100), | ||
152 | - curve: Curves.easeOutSine, | ||
153 | - ); | ||
154 | - }, | ||
155 | - child: Container( | ||
156 | - alignment: Alignment.center, | ||
157 | - height: 36.0, | ||
158 | - child: Text( | ||
159 | - ParlandoLocalizations.of(context) | ||
160 | - .profileBottomNavigationBarItemTitle, | ||
161 | - style: const TextStyle( | ||
162 | - color: Colors.white54, | ||
163 | - fontSize: 15.0, | ||
164 | - ), | ||
165 | - ), | ||
166 | - ), | ||
167 | - ), | ||
168 | - ]), | ||
169 | - ); | ||
170 | - }, | ||
171 | - ), | ||
172 | // 使用PageView的原因参看 https://zhuanlan.zhihu.com/p/58582876 | 75 | // 使用PageView的原因参看 https://zhuanlan.zhihu.com/p/58582876 |
173 | body: PageView( | 76 | body: PageView( |
174 | physics: const NeverScrollableScrollPhysics(), // 禁止滑动 | 77 | physics: const NeverScrollableScrollPhysics(), // 禁止滑动 | ... | ... |
... | @@ -34,7 +34,7 @@ class _SplashPageState extends State<SplashPage> { | ... | @@ -34,7 +34,7 @@ class _SplashPageState extends State<SplashPage> { |
34 | @override | 34 | @override |
35 | void initState() { | 35 | void initState() { |
36 | super.initState(); | 36 | super.initState(); |
37 | - WidgetsBinding.instance!.addPostFrameCallback((_) async { | 37 | + WidgetsBinding.instance.addPostFrameCallback((_) async { |
38 | /// 两种初始化方案,另一种见 main.dart | 38 | /// 两种初始化方案,另一种见 main.dart |
39 | /// 两种方法各有优劣 | 39 | /// 两种方法各有优劣 |
40 | await SpUtil.getInstance(); | 40 | await SpUtil.getInstance(); | ... | ... |
... | @@ -8,7 +8,6 @@ import 'package:flustars/flustars.dart'; | ... | @@ -8,7 +8,6 @@ import 'package:flustars/flustars.dart'; |
8 | import 'package:flutter/gestures.dart'; | 8 | import 'package:flutter/gestures.dart'; |
9 | import 'package:flutter/material.dart'; | 9 | import 'package:flutter/material.dart'; |
10 | import 'package:flutter/services.dart'; | 10 | import 'package:flutter/services.dart'; |
11 | -import 'package:Parlando/common/permission_request_widget.dart'; | ||
12 | import 'package:Parlando/common/protocol_model.dart'; | 11 | import 'package:Parlando/common/protocol_model.dart'; |
13 | import 'package:Parlando/home/webview_page.dart'; | 12 | import 'package:Parlando/home/webview_page.dart'; |
14 | import 'package:Parlando/login/widgets/my_text_field.dart'; | 13 | import 'package:Parlando/login/widgets/my_text_field.dart'; |
... | @@ -29,8 +28,6 @@ import '../login_router.dart'; | ... | @@ -29,8 +28,6 @@ import '../login_router.dart'; |
29 | import 'package:flutter_gen/gen_l10n/Parlando_localizations.dart'; | 28 | import 'package:flutter_gen/gen_l10n/Parlando_localizations.dart'; |
30 | import 'package:Parlando/extension/int_extension.dart'; | 29 | import 'package:Parlando/extension/int_extension.dart'; |
31 | 30 | ||
32 | -import 'package:permission_handler/permission_handler.dart'; | ||
33 | - | ||
34 | /// design/1注册登录/index.html | 31 | /// design/1注册登录/index.html |
35 | class LoginPage extends StatefulWidget { | 32 | class LoginPage extends StatefulWidget { |
36 | const LoginPage({Key? key}) : super(key: key); | 33 | const LoginPage({Key? key}) : super(key: key); |
... | @@ -63,41 +60,13 @@ class _LoginPageState extends State<LoginPage> | ... | @@ -63,41 +60,13 @@ class _LoginPageState extends State<LoginPage> |
63 | @override | 60 | @override |
64 | void initState() { | 61 | void initState() { |
65 | super.initState(); | 62 | super.initState(); |
66 | - WidgetsBinding.instance!.addPostFrameCallback((_) { | 63 | + WidgetsBinding.instance.addPostFrameCallback((_) { |
67 | /// 显示状态栏和导航栏 | 64 | /// 显示状态栏和导航栏 |
68 | SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, | 65 | SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, |
69 | overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); | 66 | overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); |
70 | }); | 67 | }); |
71 | _nameController.text = SpUtil.getString(Constant.phone).nullSafe; | 68 | _nameController.text = SpUtil.getString(Constant.phone).nullSafe; |
72 | - | 69 | + showPrivacyPage(); |
73 | - List<String> _list = [ | ||
74 | - "为您更好的体验应用,所以需要获取您的手机文件存储权限,以保存您的一些偏好设置", | ||
75 | - "您已拒绝权限,所以无法保存您的一些偏好设置,将无法使用APP", | ||
76 | - "您已拒绝权限,请在设置中心中同意APP的权限请求", | ||
77 | - "其他错误" | ||
78 | - ]; | ||
79 | - | ||
80 | - // Future.delayed( | ||
81 | - // Duration.zero, | ||
82 | - // () { | ||
83 | - // NavigatorUtils.pushPageByFade( | ||
84 | - // context: context, | ||
85 | - // //目标页面 | ||
86 | - // targetPage: PermissionRequestWidget( | ||
87 | - // //所需要申请的权限 | ||
88 | - // permission: Permission.camera, | ||
89 | - // //显示关闭应用按钮 | ||
90 | - // isCloseApp: true, | ||
91 | - // //提示文案 | ||
92 | - // permissionList: _list, | ||
93 | - // ), | ||
94 | - // //权限申请结果 | ||
95 | - // dismissCallBack: (value) { | ||
96 | - // showPrivacyPage(); | ||
97 | - // }, | ||
98 | - // ); | ||
99 | - // }, | ||
100 | - // ); | ||
101 | } | 70 | } |
102 | 71 | ||
103 | void showPrivacyPage() async { | 72 | void showPrivacyPage() async { | ... | ... |
... | @@ -85,7 +85,7 @@ class _PoemDetailPageState extends State<PoemDetailPage> { | ... | @@ -85,7 +85,7 @@ class _PoemDetailPageState extends State<PoemDetailPage> { |
85 | void initState() { | 85 | void initState() { |
86 | super.initState(); | 86 | super.initState(); |
87 | // 获取Build完成状态监听 | 87 | // 获取Build完成状态监听 |
88 | - WidgetsBinding.instance!.addPostFrameCallback((_) { | 88 | + WidgetsBinding.instance.addPostFrameCallback((_) { |
89 | _showHint(); | 89 | _showHint(); |
90 | }); | 90 | }); |
91 | } | 91 | } | ... | ... |
... | @@ -48,7 +48,7 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver { | ... | @@ -48,7 +48,7 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver { |
48 | 48 | ||
49 | @override | 49 | @override |
50 | void dispose() { | 50 | void dispose() { |
51 | - WidgetsBinding.instance!.removeObserver(this); | 51 | + WidgetsBinding.instance.removeObserver(this); |
52 | _videoListController.currentPlayer.pause(); | 52 | _videoListController.currentPlayer.pause(); |
53 | bus.cancel(); | 53 | bus.cancel(); |
54 | super.dispose(); | 54 | super.dispose(); |
... | @@ -57,16 +57,16 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver { | ... | @@ -57,16 +57,16 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver { |
57 | @override | 57 | @override |
58 | void initState() { | 58 | void initState() { |
59 | videoDataList = UserVideo.fetchVideo(); | 59 | videoDataList = UserVideo.fetchVideo(); |
60 | - WidgetsBinding.instance!.addObserver(this); | 60 | + WidgetsBinding.instance.addObserver(this); |
61 | _videoListController.init( | 61 | _videoListController.init( |
62 | pageController: _pageController, | 62 | pageController: _pageController, |
63 | initialList: videoDataList | 63 | initialList: videoDataList |
64 | .map( | 64 | .map( |
65 | (e) => VPVideoController( | 65 | (e) => VPVideoController( |
66 | - videoInfo: e, | 66 | + videoInfo: e, |
67 | - builder: () => VideoPlayerController.asset(e.url), | 67 | + builder: () => VideoPlayerController.asset(e.url), |
68 | - ), | 68 | + ), |
69 | - ) | 69 | + ) |
70 | .toList(), | 70 | .toList(), |
71 | videoProvider: (int index, List<VPVideoController> list) async { | 71 | videoProvider: (int index, List<VPVideoController> list) async { |
72 | return videoDataList | 72 | return videoDataList | ... | ... |
... | @@ -28,7 +28,7 @@ class LangSortBottomSheetState extends State<LangSortBottomSheet> | ... | @@ -28,7 +28,7 @@ class LangSortBottomSheetState extends State<LangSortBottomSheet> |
28 | @override | 28 | @override |
29 | void initState() { | 29 | void initState() { |
30 | super.initState(); | 30 | super.initState(); |
31 | - WidgetsBinding.instance!.addPostFrameCallback((_) { | 31 | + WidgetsBinding.instance.addPostFrameCallback((_) { |
32 | widget.provider.initData(); | 32 | widget.provider.initData(); |
33 | }); | 33 | }); |
34 | } | 34 | } | ... | ... |
... | @@ -54,7 +54,7 @@ class _AboutPageState extends State<AboutPage> { | ... | @@ -54,7 +54,7 @@ class _AboutPageState extends State<AboutPage> { |
54 | @override | 54 | @override |
55 | void initState() { | 55 | void initState() { |
56 | super.initState(); | 56 | super.initState(); |
57 | - WidgetsBinding.instance!.addPostFrameCallback((_) async { | 57 | + WidgetsBinding.instance.addPostFrameCallback((_) async { |
58 | // 2s定时器 | 58 | // 2s定时器 |
59 | _countdownTimer = Timer.periodic(const Duration(seconds: 2), (_) { | 59 | _countdownTimer = Timer.periodic(const Duration(seconds: 2), (_) { |
60 | // https://www.jianshu.com/p/e4106b829bff | 60 | // https://www.jianshu.com/p/e4106b829bff | ... | ... |
1 | import 'dart:math'; | 1 | import 'dart:math'; |
2 | +import 'package:Parlando/account/account_router.dart'; | ||
3 | +import 'package:Parlando/events/trans_event.dart'; | ||
4 | +import 'package:Parlando/home/provider/home_provider.dart'; | ||
5 | +import 'package:Parlando/poem/poem_router.dart'; | ||
6 | +import 'package:Parlando/routers/fluro_navigator.dart'; | ||
7 | +import 'package:animated_radial_menu/animated_radial_menu.dart'; | ||
2 | import 'package:flutter/material.dart'; | 8 | import 'package:flutter/material.dart'; |
3 | import 'package:flutter/rendering.dart'; | 9 | import 'package:flutter/rendering.dart'; |
4 | import 'package:Parlando/tiktok/style/style.dart'; | 10 | import 'package:Parlando/tiktok/style/style.dart'; |
11 | +import 'package:provider/provider.dart'; | ||
12 | +import 'package:flutter_gen/gen_l10n/Parlando_localizations.dart'; | ||
5 | 13 | ||
6 | const double scrollSpeed = 300; | 14 | const double scrollSpeed = 300; |
7 | 15 | ||
... | @@ -185,6 +193,82 @@ class _TikTokScaffoldState extends State<TikTokScaffold> | ... | @@ -185,6 +193,82 @@ class _TikTokScaffoldState extends State<TikTokScaffold> |
185 | body: body, | 193 | body: body, |
186 | backgroundColor: Colors.black, | 194 | backgroundColor: Colors.black, |
187 | resizeToAvoidBottomInset: false, | 195 | resizeToAvoidBottomInset: false, |
196 | + floatingActionButton: SizedBox( | ||
197 | + height: 60, | ||
198 | + child: RadialMenu( | ||
199 | + children: [ | ||
200 | + RadialButton( | ||
201 | + icon: const Icon(Icons.video_call_outlined), | ||
202 | + buttonColor: Colors.teal, | ||
203 | + onPress: () { | ||
204 | + eventBus.fire(TransEvent()); | ||
205 | + NavigatorUtils.push( | ||
206 | + context, | ||
207 | + '${PoemRouter.poemRecordVideoPage}?data=100', | ||
208 | + ); | ||
209 | + }), | ||
210 | + RadialButton( | ||
211 | + icon: const Icon(Icons.mic_none_outlined), | ||
212 | + buttonColor: Colors.green, | ||
213 | + onPress: () { | ||
214 | + eventBus.fire(TransEvent()); | ||
215 | + NavigatorUtils.push( | ||
216 | + context, | ||
217 | + '${PoemRouter.poemRecordAudioPage}?id=100', | ||
218 | + ); | ||
219 | + }), | ||
220 | + ], | ||
221 | + ), | ||
222 | + ), | ||
223 | + bottomNavigationBar: Consumer<HomeProvider>( | ||
224 | + builder: (_, provider, __) { | ||
225 | + return BottomAppBar( | ||
226 | + color: Colors.grey, | ||
227 | + child: Row( | ||
228 | + mainAxisSize: MainAxisSize.max, | ||
229 | + mainAxisAlignment: MainAxisAlignment.spaceAround, | ||
230 | + children: <Widget>[ | ||
231 | + InkWell( | ||
232 | + onTap: () {}, | ||
233 | + child: Container( | ||
234 | + alignment: Alignment.center, | ||
235 | + height: 36.0, | ||
236 | + child: Text( | ||
237 | + ParlandoLocalizations.of(context) | ||
238 | + .onePoemBottomNavigationBarItemTitle, | ||
239 | + style: const TextStyle( | ||
240 | + color: Colors.white54, | ||
241 | + fontSize: 15.0, | ||
242 | + ), | ||
243 | + ), | ||
244 | + ), | ||
245 | + ), | ||
246 | + InkWell( | ||
247 | + onTap: () { | ||
248 | + eventBus.fire(TransEvent()); | ||
249 | + NavigatorUtils.push( | ||
250 | + context, | ||
251 | + AccountRouter.accountPage, | ||
252 | + ); | ||
253 | + }, | ||
254 | + child: Container( | ||
255 | + alignment: Alignment.center, | ||
256 | + height: 36.0, | ||
257 | + child: Text( | ||
258 | + ParlandoLocalizations.of(context) | ||
259 | + .profileBottomNavigationBarItemTitle, | ||
260 | + style: const TextStyle( | ||
261 | + color: Colors.white54, | ||
262 | + fontSize: 15.0, | ||
263 | + ), | ||
264 | + ), | ||
265 | + ), | ||
266 | + ), | ||
267 | + ]), | ||
268 | + ); | ||
269 | + }, | ||
270 | + ), | ||
271 | + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, | ||
188 | ), | 272 | ), |
189 | ); | 273 | ); |
190 | return body; | 274 | return body; |
... | @@ -344,6 +428,7 @@ class _MiddlePage extends StatelessWidget { | ... | @@ -344,6 +428,7 @@ class _MiddlePage extends StatelessWidget { |
344 | required this.tabBar, | 428 | required this.tabBar, |
345 | this.page, | 429 | this.page, |
346 | }) : super(key: key); | 430 | }) : super(key: key); |
431 | + | ||
347 | @override | 432 | @override |
348 | Widget build(BuildContext context) { | 433 | Widget build(BuildContext context) { |
349 | Widget mainVideoList = Container( | 434 | Widget mainVideoList = Container( |
... | @@ -440,6 +525,7 @@ class _LeftPageTransform extends StatelessWidget { | ... | @@ -440,6 +525,7 @@ class _LeftPageTransform extends StatelessWidget { |
440 | 525 | ||
441 | const _LeftPageTransform({Key? key, this.offsetX, this.content}) | 526 | const _LeftPageTransform({Key? key, this.offsetX, this.content}) |
442 | : super(key: key); | 527 | : super(key: key); |
528 | + | ||
443 | @override | 529 | @override |
444 | Widget build(BuildContext context) { | 530 | Widget build(BuildContext context) { |
445 | final screenWidth = MediaQuery.of(context).size.width; | 531 | final screenWidth = MediaQuery.of(context).size.width; | ... | ... |
1 | -import 'dart:math' as Math; | ||
2 | - | ||
3 | -import 'package:flutter/material.dart'; | ||
4 | - | ||
5 | -/// Draws an [ActionIcon] and [_ArcProgressPainter] that represent an active action. | ||
6 | -/// As the provided [Animation] progresses the ActionArc grows into a full | ||
7 | -/// circle and the ActionIcon moves along it. | ||
8 | -class ArcProgressIndicator extends StatelessWidget { | ||
9 | - // required | ||
10 | - final Animation<double> controller; | ||
11 | - final double radius; | ||
12 | - | ||
13 | - // optional | ||
14 | - final double startAngle; | ||
15 | - final double? width; | ||
16 | - | ||
17 | - /// The color to use when filling the arc. | ||
18 | - /// | ||
19 | - /// Defaults to the accent color of the current theme. | ||
20 | - final Color? color; | ||
21 | - final IconData icon; | ||
22 | - final Color? iconColor; | ||
23 | - final double? iconSize; | ||
24 | - | ||
25 | - // private | ||
26 | - final Animation<double> _progress; | ||
27 | - | ||
28 | - ArcProgressIndicator({ | ||
29 | - Key? key, | ||
30 | - required this.controller, | ||
31 | - required this.radius, | ||
32 | - this.startAngle = 0.0, | ||
33 | - this.width, | ||
34 | - this.color, | ||
35 | - required this.icon, | ||
36 | - this.iconColor, | ||
37 | - this.iconSize, | ||
38 | - }) : _progress = Tween(begin: 0.0, end: 1.0).animate(controller), | ||
39 | - super(key: key); | ||
40 | - | ||
41 | - @override | ||
42 | - Widget build(BuildContext context) { | ||
43 | - late TextPainter _iconPainter; | ||
44 | - final ThemeData theme = Theme.of(context); | ||
45 | - final Color? _iconColor = iconColor ?? theme.colorScheme.secondary; | ||
46 | - final double? _iconSize = iconSize ?? IconTheme.of(context).size; | ||
47 | - | ||
48 | - _iconPainter = TextPainter( | ||
49 | - textDirection: Directionality.of(context), | ||
50 | - text: TextSpan( | ||
51 | - text: String.fromCharCode(icon.codePoint), | ||
52 | - style: TextStyle( | ||
53 | - inherit: false, | ||
54 | - color: _iconColor, | ||
55 | - fontSize: _iconSize, | ||
56 | - fontFamily: icon.fontFamily, | ||
57 | - package: icon.fontPackage, | ||
58 | - ), | ||
59 | - ), | ||
60 | - )..layout(); | ||
61 | - | ||
62 | - return CustomPaint( | ||
63 | - painter: _ArcProgressPainter( | ||
64 | - controller: _progress, | ||
65 | - color: color ?? theme.colorScheme.secondary, | ||
66 | - radius: radius, | ||
67 | - width: width ?? _iconSize! * 2, | ||
68 | - startAngle: startAngle, | ||
69 | - icon: _iconPainter, | ||
70 | - ), | ||
71 | - ); | ||
72 | - } | ||
73 | -} | ||
74 | - | ||
75 | -class _ArcProgressPainter extends CustomPainter { | ||
76 | - // required | ||
77 | - final Animation<double> controller; | ||
78 | - final Color color; | ||
79 | - final double radius; | ||
80 | - final double width; | ||
81 | - | ||
82 | - // optional | ||
83 | - final double startAngle; | ||
84 | - final TextPainter icon; | ||
85 | - | ||
86 | - _ArcProgressPainter({ | ||
87 | - required this.controller, | ||
88 | - required this.color, | ||
89 | - required this.radius, | ||
90 | - required this.width, | ||
91 | - this.startAngle = 0.0, | ||
92 | - required this.icon, | ||
93 | - }) : super(repaint: controller); | ||
94 | - | ||
95 | - @override | ||
96 | - void paint(Canvas canvas, Size size) { | ||
97 | - Paint paint = Paint() | ||
98 | - ..color = color | ||
99 | - ..strokeWidth = width | ||
100 | - ..strokeCap = StrokeCap.round | ||
101 | - ..style = PaintingStyle.stroke; | ||
102 | - | ||
103 | - final double sweepAngle = controller.value * 2 * Math.pi; | ||
104 | - | ||
105 | - canvas.drawArc( | ||
106 | - Offset.zero & size, | ||
107 | - startAngle, | ||
108 | - sweepAngle, | ||
109 | - false, | ||
110 | - paint, | ||
111 | - ); | ||
112 | - | ||
113 | - double angle = startAngle + sweepAngle; | ||
114 | - Offset offset = Offset( | ||
115 | - (size.width / 2 - icon.size.width / 2) + radius * Math.cos(angle), | ||
116 | - (size.height / 2 - icon.size.height / 2) + radius * Math.sin(angle), | ||
117 | - ); | ||
118 | - | ||
119 | - icon.paint(canvas, offset); | ||
120 | - } | ||
121 | - | ||
122 | - @override | ||
123 | - bool shouldRepaint(_ArcProgressPainter other) { | ||
124 | - return controller.value != other.controller.value || | ||
125 | - color != other.color || | ||
126 | - radius != other.radius || | ||
127 | - width != other.width || | ||
128 | - startAngle != other.startAngle || | ||
129 | - icon != other.icon; | ||
130 | - } | ||
131 | -} |
1 | -import 'dart:async'; | ||
2 | -import 'dart:math' as math; | ||
3 | - | ||
4 | -import 'package:flutter/material.dart'; | ||
5 | -import 'package:Parlando/widgets/radial/src/radial_menu_button.dart'; | ||
6 | -import 'package:Parlando/widgets/radial/src/radial_menu_center_button.dart'; | ||
7 | -import 'package:Parlando/widgets/radial/src/radial_menu_item.dart'; | ||
8 | - | ||
9 | -const double _radiansPerDegree = math.pi / 180; | ||
10 | -const double _startAngle = -120.0 * _radiansPerDegree; | ||
11 | - | ||
12 | -typedef ItemAngleCalculator = double Function(int index); | ||
13 | - | ||
14 | -/// A radial menu for selecting from a list of items. | ||
15 | -/// | ||
16 | -/// A radial menu lets the user select from a number of items. It displays a | ||
17 | -/// button that opens the menu, showing its items arranged in an arc. Selecting | ||
18 | -/// an item triggers the animation of a progress bar drawn at the specified | ||
19 | -/// [radius] around the central menu button. | ||
20 | -/// | ||
21 | -/// The type `T` is the type of the values the radial menu represents. All the | ||
22 | -/// entries in a given menu must represent values with consistent types. | ||
23 | -/// Typically, an enum is used. Each [RadialMenuItem] in [items] must be | ||
24 | -/// specialized with that same type argument. | ||
25 | -/// | ||
26 | -/// Requires one of its ancestors to be a [Material] widget. | ||
27 | -/// | ||
28 | -/// See also: | ||
29 | -/// | ||
30 | -/// * [RadialMenuItem], the widget used to represent the [items]. | ||
31 | -/// * [RadialMenuCenterButton], the button used to open and close the menu. | ||
32 | -class RadialMenu<T> extends StatefulWidget { | ||
33 | - /// Creates a dropdown button. | ||
34 | - /// | ||
35 | - /// The [items] must have distinct values. | ||
36 | - /// | ||
37 | - /// The [radius], [menuAnimationDuration], and [progressAnimationDuration] | ||
38 | - /// arguments must not be null (they all have defaults, so do not need to be | ||
39 | - /// specified). | ||
40 | - const RadialMenu({ | ||
41 | - Key? key, | ||
42 | - required this.items, | ||
43 | - required this.onSelected, | ||
44 | - this.radius = 100.0, | ||
45 | - this.menuAnimationDuration = const Duration(milliseconds: 1000), | ||
46 | - this.progressAnimationDuration = const Duration(milliseconds: 1000), | ||
47 | - }) : super(key: key); | ||
48 | - | ||
49 | - /// The list of possible items to select among. | ||
50 | - final List<RadialMenuItem<T>> items; | ||
51 | - | ||
52 | - /// Called when the user selects an item. | ||
53 | - final Function onSelected; // TODO why Function? not ValueChanged? | ||
54 | - | ||
55 | - /// The radius of the arc used to lay out the items and draw the progress bar. | ||
56 | - /// | ||
57 | - /// Defaults to 100.0. | ||
58 | - final double radius; | ||
59 | - | ||
60 | - /// Duration of the menu opening/closing animation. | ||
61 | - /// | ||
62 | - /// Defaults to 1000 milliseconds. | ||
63 | - final Duration menuAnimationDuration; | ||
64 | - | ||
65 | - /// Duration of the action activation progress arc animation. | ||
66 | - /// | ||
67 | - /// Defaults to 1000 milliseconds. | ||
68 | - final Duration progressAnimationDuration; | ||
69 | - | ||
70 | - @override | ||
71 | - RadialMenuState createState() => RadialMenuState(); | ||
72 | -} | ||
73 | - | ||
74 | -class RadialMenuState extends State<RadialMenu> with TickerProviderStateMixin { | ||
75 | - late AnimationController _menuAnimationController; | ||
76 | - late AnimationController _progressAnimationController; | ||
77 | - bool _isOpen = false; | ||
78 | - int _activeItemIndex = -1; | ||
79 | - | ||
80 | - // todo: xqwzts: allow users to pass in their own calculator as a param. | ||
81 | - // and change this to the default: radialItemAngleCalculator. | ||
82 | - double calculateItemAngle(int index) { | ||
83 | - double _itemSpacing = 120.0 / widget.items.length; | ||
84 | - return _startAngle + index * _itemSpacing * _radiansPerDegree; | ||
85 | - } | ||
86 | - | ||
87 | - @override | ||
88 | - void initState() { | ||
89 | - super.initState(); | ||
90 | - _menuAnimationController = AnimationController( | ||
91 | - duration: widget.menuAnimationDuration, | ||
92 | - vsync: this, | ||
93 | - ); | ||
94 | - _progressAnimationController = AnimationController( | ||
95 | - duration: widget.progressAnimationDuration, | ||
96 | - vsync: this, | ||
97 | - ); | ||
98 | - } | ||
99 | - | ||
100 | - @override | ||
101 | - void dispose() { | ||
102 | - _menuAnimationController.dispose(); | ||
103 | - _progressAnimationController.dispose(); | ||
104 | - super.dispose(); | ||
105 | - } | ||
106 | - | ||
107 | - void _openMenu() { | ||
108 | - _menuAnimationController.forward(); | ||
109 | - setState(() => _isOpen = true); | ||
110 | - } | ||
111 | - | ||
112 | - void _closeMenu() { | ||
113 | - _menuAnimationController.reverse(); | ||
114 | - setState(() => _isOpen = false); | ||
115 | - } | ||
116 | - | ||
117 | - Future<void> _activate(int itemIndex) async { | ||
118 | - setState(() => _activeItemIndex = itemIndex); | ||
119 | - await _progressAnimationController.forward().orCancel; | ||
120 | - widget.onSelected(widget.items[itemIndex].value); | ||
121 | - _closeMenu(); | ||
122 | - } | ||
123 | - | ||
124 | - /// Resets the menu to its initial (closed) state. | ||
125 | - void reset() { | ||
126 | - _menuAnimationController.reset(); | ||
127 | - _progressAnimationController.reverse(); | ||
128 | - setState(() { | ||
129 | - _isOpen = false; | ||
130 | - _activeItemIndex = -1; | ||
131 | - }); | ||
132 | - } | ||
133 | - | ||
134 | - Widget _buildActionButton(int index) { | ||
135 | - final RadialMenuItem item = widget.items[index]; | ||
136 | - | ||
137 | - return LayoutId( | ||
138 | - id: '${_RadialMenuLayout.actionButton}$index', | ||
139 | - child: RadialMenuButton( | ||
140 | - child: item, | ||
141 | - backgroundColor: item.backgroundColor, | ||
142 | - onPressed: () => _activate(index), | ||
143 | - ), | ||
144 | - ); | ||
145 | - } | ||
146 | - | ||
147 | - Widget _buildCenterButton() { | ||
148 | - return LayoutId( | ||
149 | - id: _RadialMenuLayout.menuButton, | ||
150 | - child: RadialMenuCenterButton( | ||
151 | - openCloseAnimationController: _menuAnimationController.view, | ||
152 | - activateAnimationController: _progressAnimationController.view, | ||
153 | - isOpen: _isOpen, | ||
154 | - onPressed: _isOpen ? _closeMenu : _openMenu, | ||
155 | - ), | ||
156 | - ); | ||
157 | - } | ||
158 | - | ||
159 | - @override | ||
160 | - Widget build(BuildContext context) { | ||
161 | - final List<Widget> children = <Widget>[]; | ||
162 | - for (int i = 0; i < widget.items.length; i++) { | ||
163 | - if (_activeItemIndex != i) { | ||
164 | - children.add(_buildActionButton(i)); | ||
165 | - } | ||
166 | - } | ||
167 | - children.add(_buildCenterButton()); | ||
168 | - | ||
169 | - return AnimatedBuilder( | ||
170 | - animation: _menuAnimationController, | ||
171 | - builder: (BuildContext context, Widget? child) { | ||
172 | - return CustomMultiChildLayout( | ||
173 | - delegate: _RadialMenuLayout( | ||
174 | - itemCount: widget.items.length, | ||
175 | - radius: widget.radius, | ||
176 | - calculateItemAngle: calculateItemAngle, | ||
177 | - controller: _menuAnimationController.view, | ||
178 | - ), | ||
179 | - children: children, | ||
180 | - ); | ||
181 | - }, | ||
182 | - ); | ||
183 | - } | ||
184 | -} | ||
185 | - | ||
186 | -class _RadialMenuLayout extends MultiChildLayoutDelegate { | ||
187 | - static const String menuButton = 'menuButton'; | ||
188 | - static const String actionButton = 'actionButton'; | ||
189 | - static const String activeAction = 'activeAction'; | ||
190 | - | ||
191 | - final int itemCount; | ||
192 | - final double radius; | ||
193 | - final ItemAngleCalculator calculateItemAngle; | ||
194 | - | ||
195 | - final Animation<double> controller; | ||
196 | - | ||
197 | - final Animation<double> _progress; | ||
198 | - | ||
199 | - _RadialMenuLayout({ | ||
200 | - required this.itemCount, | ||
201 | - required this.radius, | ||
202 | - required this.calculateItemAngle, | ||
203 | - required this.controller, | ||
204 | - }) : _progress = Tween<double>(begin: 0.0, end: radius).animate( | ||
205 | - CurvedAnimation( | ||
206 | - curve: Curves.elasticOut, | ||
207 | - parent: controller, | ||
208 | - ), | ||
209 | - ); | ||
210 | - | ||
211 | - late Offset center; | ||
212 | - | ||
213 | - @override | ||
214 | - void performLayout(Size size) { | ||
215 | - center = Offset(size.width / 2, size.height / 2); | ||
216 | - | ||
217 | - if (hasChild(menuButton)) { | ||
218 | - Size menuButtonSize; | ||
219 | - menuButtonSize = layoutChild(menuButton, BoxConstraints.loose(size)); | ||
220 | - | ||
221 | - // place the menubutton in the center | ||
222 | - positionChild( | ||
223 | - menuButton, | ||
224 | - Offset( | ||
225 | - center.dx - menuButtonSize.width / 2, | ||
226 | - center.dy - menuButtonSize.height / 2, | ||
227 | - ), | ||
228 | - ); | ||
229 | - } | ||
230 | - | ||
231 | - for (int i = 0; i < itemCount; i++) { | ||
232 | - final String actionButtonId = '$actionButton$i'; | ||
233 | - final String actionArcId = '$activeAction$i'; | ||
234 | - if (hasChild(actionArcId)) { | ||
235 | - final Size arcSize = layoutChild( | ||
236 | - actionArcId, | ||
237 | - BoxConstraints.expand( | ||
238 | - width: _progress.value * 2, | ||
239 | - height: _progress.value * 2, | ||
240 | - ), | ||
241 | - ); | ||
242 | - | ||
243 | - positionChild( | ||
244 | - actionArcId, | ||
245 | - Offset( | ||
246 | - center.dx - arcSize.width / 2, | ||
247 | - center.dy - arcSize.height / 2, | ||
248 | - ), | ||
249 | - ); | ||
250 | - } | ||
251 | - | ||
252 | - if (hasChild(actionButtonId)) { | ||
253 | - final Size buttonSize = | ||
254 | - layoutChild(actionButtonId, BoxConstraints.loose(size)); | ||
255 | - | ||
256 | - final double itemAngle = calculateItemAngle(i); | ||
257 | - | ||
258 | - positionChild( | ||
259 | - actionButtonId, | ||
260 | - Offset( | ||
261 | - (center.dx - buttonSize.width / 2) + | ||
262 | - (_progress.value) * math.cos(itemAngle), | ||
263 | - (center.dy - buttonSize.height / 2) + | ||
264 | - (_progress.value) * math.sin(itemAngle), | ||
265 | - ), | ||
266 | - ); | ||
267 | - } | ||
268 | - } | ||
269 | - } | ||
270 | - | ||
271 | - @override | ||
272 | - bool shouldRelayout(_RadialMenuLayout oldDelegate) => | ||
273 | - itemCount != oldDelegate.itemCount || | ||
274 | - radius != oldDelegate.radius || | ||
275 | - calculateItemAngle != oldDelegate.calculateItemAngle || | ||
276 | - controller != oldDelegate.controller || | ||
277 | - _progress != oldDelegate._progress; | ||
278 | -} |
1 | -import 'package:flutter/foundation.dart'; | ||
2 | -import 'package:flutter/material.dart'; | ||
3 | - | ||
4 | -class RadialMenuButton extends StatelessWidget { | ||
5 | - const RadialMenuButton({ | ||
6 | - Key? key, | ||
7 | - required this.child, | ||
8 | - required this.backgroundColor, | ||
9 | - required this.onPressed, | ||
10 | - }) : super(key: key); | ||
11 | - | ||
12 | - final Widget child; | ||
13 | - final Color backgroundColor; | ||
14 | - final VoidCallback onPressed; | ||
15 | - | ||
16 | - @override | ||
17 | - Widget build(BuildContext context) { | ||
18 | - final Color color = backgroundColor; | ||
19 | - | ||
20 | - return Semantics( | ||
21 | - button: true, | ||
22 | - enabled: true, | ||
23 | - child: Material( | ||
24 | - type: MaterialType.circle, | ||
25 | - color: color, | ||
26 | - child: InkWell( | ||
27 | - onTap: onPressed, | ||
28 | - child: child, | ||
29 | - ), | ||
30 | - ), | ||
31 | - ); | ||
32 | - } | ||
33 | -} |
1 | -import 'package:flutter/foundation.dart'; | ||
2 | -import 'package:flutter/material.dart'; | ||
3 | -import 'package:Parlando/widgets/radial/src/radial_menu_button.dart'; | ||
4 | - | ||
5 | -const double _defaultButtonSize = 48.0; | ||
6 | - | ||
7 | -/// The button at the center of a [RadialMenu] which controls its open/closed | ||
8 | -/// state. | ||
9 | -class RadialMenuCenterButton extends StatelessWidget { | ||
10 | - /// Drives the opening/closing animation of the [RadialMenu]. | ||
11 | - final Animation<double> openCloseAnimationController; | ||
12 | - | ||
13 | - /// Drives the animation when an item in the [RadialMenu] is pressed. | ||
14 | - final Animation<double> activateAnimationController; | ||
15 | - | ||
16 | - /// Called when the user presses this button. | ||
17 | - final VoidCallback onPressed; | ||
18 | - | ||
19 | - /// The opened/closed state of the menu. | ||
20 | - /// | ||
21 | - /// Determines which of [closedColor] or [openedColor] should be used as the | ||
22 | - /// background color of the button. | ||
23 | - final bool isOpen; | ||
24 | - | ||
25 | - /// The color to use when painting the icon. | ||
26 | - /// | ||
27 | - /// Defaults to [Colors.black]. | ||
28 | - final Color iconColor; | ||
29 | - | ||
30 | - /// Background color when it is in its closed state. | ||
31 | - /// | ||
32 | - /// Defaults to [Colors.white]. | ||
33 | - final Color closedColor; | ||
34 | - | ||
35 | - /// Background color when it is in its opened state. | ||
36 | - /// | ||
37 | - /// Defaults to [Colors.grey]. | ||
38 | - final Color openedColor; | ||
39 | - | ||
40 | - /// The size of the button. | ||
41 | - /// | ||
42 | - /// Defaults to 48.0. | ||
43 | - final double size; | ||
44 | - | ||
45 | - /// The animation progress for the [AnimatedIcon] in the center of the button. | ||
46 | - final Animation<double> _progress; | ||
47 | - | ||
48 | - /// The scale factor applied to the button. | ||
49 | - /// | ||
50 | - /// Animates from 1.0 to 0.0 when an an item is pressed in the menu and | ||
51 | - /// [activateAnimationController] progresses. | ||
52 | - final Animation<double> _scale; | ||
53 | - | ||
54 | - RadialMenuCenterButton({ | ||
55 | - Key? key, | ||
56 | - required this.openCloseAnimationController, | ||
57 | - required this.activateAnimationController, | ||
58 | - required this.onPressed, | ||
59 | - required this.isOpen, | ||
60 | - this.iconColor = Colors.black, | ||
61 | - this.closedColor = Colors.white, | ||
62 | - this.openedColor = Colors.grey, | ||
63 | - this.size = _defaultButtonSize, | ||
64 | - }) : _progress = Tween(begin: 0.0, end: 1.0).animate( | ||
65 | - CurvedAnimation( | ||
66 | - parent: openCloseAnimationController, | ||
67 | - curve: const Interval( | ||
68 | - 0.0, | ||
69 | - 0.5, | ||
70 | - curve: Curves.ease, | ||
71 | - ), | ||
72 | - ), | ||
73 | - ), | ||
74 | - _scale = Tween(begin: 1.0, end: 0.0).animate( | ||
75 | - CurvedAnimation( | ||
76 | - parent: activateAnimationController, | ||
77 | - curve: Curves.elasticIn, | ||
78 | - ), | ||
79 | - ), | ||
80 | - super(key: key); | ||
81 | - | ||
82 | - @override | ||
83 | - Widget build(BuildContext context) { | ||
84 | - final AnimatedIcon animatedIcon = AnimatedIcon( | ||
85 | - color: iconColor, | ||
86 | - icon: AnimatedIcons.menu_close, | ||
87 | - progress: _progress, | ||
88 | - ); | ||
89 | - | ||
90 | - final Widget child = SizedBox( | ||
91 | - width: size, | ||
92 | - height: size, | ||
93 | - child: Center( | ||
94 | - child: animatedIcon, | ||
95 | - ), | ||
96 | - ); | ||
97 | - | ||
98 | - final Color color = isOpen ? openedColor : closedColor; | ||
99 | - | ||
100 | - return ScaleTransition( | ||
101 | - scale: _scale, | ||
102 | - child: RadialMenuButton( | ||
103 | - child: child, | ||
104 | - backgroundColor: color, | ||
105 | - onPressed: onPressed, | ||
106 | - ), | ||
107 | - ); | ||
108 | - } | ||
109 | -} |
1 | -import 'package:flutter/foundation.dart'; | ||
2 | -import 'package:flutter/material.dart'; | ||
3 | - | ||
4 | -const double _defaultButtonSize = 48.0; | ||
5 | - | ||
6 | -/// An item in a [RadialMenu]. | ||
7 | -/// | ||
8 | -/// The type `T` is the type of the value the entry represents. All the entries | ||
9 | -/// in a given menu must represent values with consistent types. | ||
10 | -class RadialMenuItem<T> extends StatelessWidget { | ||
11 | - /// Creates a circular action button for an item in a [RadialMenu]. | ||
12 | - /// | ||
13 | - /// The [child] argument is required. | ||
14 | - const RadialMenuItem({ | ||
15 | - Key? key, | ||
16 | - required this.child, | ||
17 | - required this.value, | ||
18 | - required this.tooltip, | ||
19 | - this.size = _defaultButtonSize, | ||
20 | - required this.backgroundColor, | ||
21 | - required this.iconColor, | ||
22 | - this.iconSize = 24.0, | ||
23 | - }) : super(key: key); | ||
24 | - | ||
25 | - /// The widget below this widget in the tree. | ||
26 | - /// | ||
27 | - /// Typically an [Icon] widget. | ||
28 | - final Widget child; | ||
29 | - | ||
30 | - /// The value to return if the user selects this menu item. | ||
31 | - /// | ||
32 | - /// Eventually returned in a call to [RadialMenu.onSelected]. | ||
33 | - final T value; | ||
34 | - | ||
35 | - /// Text that describes the action that will occur when the button is pressed. | ||
36 | - /// | ||
37 | - /// This text is displayed when the user long-presses on the button and is | ||
38 | - /// used for accessibility. | ||
39 | - final String tooltip; | ||
40 | - | ||
41 | - /// The color to use when filling the button. | ||
42 | - /// | ||
43 | - /// Defaults to the primary color of the current theme. | ||
44 | - final Color backgroundColor; | ||
45 | - | ||
46 | - /// The size of the button. | ||
47 | - /// | ||
48 | - /// Defaults to 48.0. | ||
49 | - final double size; | ||
50 | - | ||
51 | - /// The color to use when painting the child icon. | ||
52 | - /// | ||
53 | - /// Defaults to the primary icon theme color. | ||
54 | - final Color? iconColor; | ||
55 | - | ||
56 | - final double? iconSize; | ||
57 | - | ||
58 | - @override | ||
59 | - Widget build(BuildContext context) { | ||
60 | - final Color? _iconColor = | ||
61 | - iconColor ?? Theme.of(context).primaryIconTheme.color; | ||
62 | - | ||
63 | - late Widget result; | ||
64 | - | ||
65 | - result = Center( | ||
66 | - child: IconTheme.merge( | ||
67 | - data: IconThemeData( | ||
68 | - color: _iconColor, | ||
69 | - size: iconSize, | ||
70 | - ), | ||
71 | - child: child, | ||
72 | - ), | ||
73 | - ); | ||
74 | - | ||
75 | - result = Tooltip( | ||
76 | - message: tooltip, | ||
77 | - child: result, | ||
78 | - ); | ||
79 | - | ||
80 | - result = SizedBox( | ||
81 | - width: size, | ||
82 | - height: size, | ||
83 | - child: result, | ||
84 | - ); | ||
85 | - | ||
86 | - return result; | ||
87 | - } | ||
88 | -} |
... | @@ -107,7 +107,7 @@ class _SearchBarState extends State<SearchBar> { | ... | @@ -107,7 +107,7 @@ class _SearchBarState extends State<SearchBar> { |
107 | ), | 107 | ), |
108 | onTap: () { | 108 | onTap: () { |
109 | /// https://github.com/flutter/flutter/issues/35848 | 109 | /// https://github.com/flutter/flutter/issues/35848 |
110 | - SchedulerBinding.instance!.addPostFrameCallback((_) { | 110 | + SchedulerBinding.instance.addPostFrameCallback((_) { |
111 | _controller.text = ''; | 111 | _controller.text = ''; |
112 | }); | 112 | }); |
113 | }, | 113 | }, | ... | ... |
... | @@ -15,13 +15,20 @@ packages: | ... | @@ -15,13 +15,20 @@ packages: |
15 | url: "https://pub.flutter-io.cn" | 15 | url: "https://pub.flutter-io.cn" |
16 | source: hosted | 16 | source: hosted |
17 | version: "2.8.0" | 17 | version: "2.8.0" |
18 | + animated_radial_menu: | ||
19 | + dependency: "direct main" | ||
20 | + description: | ||
21 | + name: animated_radial_menu | ||
22 | + url: "https://pub.flutter-io.cn" | ||
23 | + source: hosted | ||
24 | + version: "0.0.1" | ||
18 | archive: | 25 | archive: |
19 | dependency: transitive | 26 | dependency: transitive |
20 | description: | 27 | description: |
21 | name: archive | 28 | name: archive |
22 | url: "https://pub.flutter-io.cn" | 29 | url: "https://pub.flutter-io.cn" |
23 | source: hosted | 30 | source: hosted |
24 | - version: "3.1.6" | 31 | + version: "3.1.11" |
25 | args: | 32 | args: |
26 | dependency: transitive | 33 | dependency: transitive |
27 | description: | 34 | description: |
... | @@ -189,7 +196,7 @@ packages: | ... | @@ -189,7 +196,7 @@ packages: |
189 | name: collection | 196 | name: collection |
190 | url: "https://pub.flutter-io.cn" | 197 | url: "https://pub.flutter-io.cn" |
191 | source: hosted | 198 | source: hosted |
192 | - version: "1.15.0" | 199 | + version: "1.16.0" |
193 | common_utils: | 200 | common_utils: |
194 | dependency: "direct main" | 201 | dependency: "direct main" |
195 | description: | 202 | description: |
... | @@ -210,7 +217,7 @@ packages: | ... | @@ -210,7 +217,7 @@ packages: |
210 | name: coverage | 217 | name: coverage |
211 | url: "https://pub.flutter-io.cn" | 218 | url: "https://pub.flutter-io.cn" |
212 | source: hosted | 219 | source: hosted |
213 | - version: "1.0.3" | 220 | + version: "1.2.0" |
214 | cross_file: | 221 | cross_file: |
215 | dependency: transitive | 222 | dependency: transitive |
216 | description: | 223 | description: |
... | @@ -322,7 +329,7 @@ packages: | ... | @@ -322,7 +329,7 @@ packages: |
322 | name: fake_async | 329 | name: fake_async |
323 | url: "https://pub.flutter-io.cn" | 330 | url: "https://pub.flutter-io.cn" |
324 | source: hosted | 331 | source: hosted |
325 | - version: "1.2.0" | 332 | + version: "1.3.0" |
326 | ffi: | 333 | ffi: |
327 | dependency: transitive | 334 | dependency: transitive |
328 | description: | 335 | description: |
... | @@ -411,7 +418,7 @@ packages: | ... | @@ -411,7 +418,7 @@ packages: |
411 | name: flutter_lints | 418 | name: flutter_lints |
412 | url: "https://pub.flutter-io.cn" | 419 | url: "https://pub.flutter-io.cn" |
413 | source: hosted | 420 | source: hosted |
414 | - version: "1.0.4" | 421 | + version: "2.0.1" |
415 | flutter_localizations: | 422 | flutter_localizations: |
416 | dependency: "direct main" | 423 | dependency: "direct main" |
417 | description: flutter | 424 | description: flutter |
... | @@ -483,6 +490,13 @@ packages: | ... | @@ -483,6 +490,13 @@ packages: |
483 | description: flutter | 490 | description: flutter |
484 | source: sdk | 491 | source: sdk |
485 | version: "0.0.0" | 492 | version: "0.0.0" |
493 | + font_awesome_flutter: | ||
494 | + dependency: transitive | ||
495 | + description: | ||
496 | + name: font_awesome_flutter | ||
497 | + url: "https://pub.flutter-io.cn" | ||
498 | + source: hosted | ||
499 | + version: "9.2.0" | ||
486 | frontend_server_client: | 500 | frontend_server_client: |
487 | dependency: transitive | 501 | dependency: transitive |
488 | description: | 502 | description: |
... | @@ -632,7 +646,7 @@ packages: | ... | @@ -632,7 +646,7 @@ packages: |
632 | name: js | 646 | name: js |
633 | url: "https://pub.flutter-io.cn" | 647 | url: "https://pub.flutter-io.cn" |
634 | source: hosted | 648 | source: hosted |
635 | - version: "0.6.3" | 649 | + version: "0.6.4" |
636 | json_annotation: | 650 | json_annotation: |
637 | dependency: "direct main" | 651 | dependency: "direct main" |
638 | description: | 652 | description: |
... | @@ -653,7 +667,7 @@ packages: | ... | @@ -653,7 +667,7 @@ packages: |
653 | name: keyboard_actions | 667 | name: keyboard_actions |
654 | url: "https://pub.flutter-io.cn" | 668 | url: "https://pub.flutter-io.cn" |
655 | source: hosted | 669 | source: hosted |
656 | - version: "3.4.7" | 670 | + version: "4.0.0" |
657 | lint: | 671 | lint: |
658 | dependency: transitive | 672 | dependency: transitive |
659 | description: | 673 | description: |
... | @@ -667,7 +681,7 @@ packages: | ... | @@ -667,7 +681,7 @@ packages: |
667 | name: lints | 681 | name: lints |
668 | url: "https://pub.flutter-io.cn" | 682 | url: "https://pub.flutter-io.cn" |
669 | source: hosted | 683 | source: hosted |
670 | - version: "1.0.1" | 684 | + version: "2.0.0" |
671 | logger: | 685 | logger: |
672 | dependency: transitive | 686 | dependency: transitive |
673 | description: | 687 | description: |
... | @@ -695,7 +709,7 @@ packages: | ... | @@ -695,7 +709,7 @@ packages: |
695 | name: material_color_utilities | 709 | name: material_color_utilities |
696 | url: "https://pub.flutter-io.cn" | 710 | url: "https://pub.flutter-io.cn" |
697 | source: hosted | 711 | source: hosted |
698 | - version: "0.1.3" | 712 | + version: "0.1.4" |
699 | meta: | 713 | meta: |
700 | dependency: transitive | 714 | dependency: transitive |
701 | description: | 715 | description: |
... | @@ -751,7 +765,7 @@ packages: | ... | @@ -751,7 +765,7 @@ packages: |
751 | name: path | 765 | name: path |
752 | url: "https://pub.flutter-io.cn" | 766 | url: "https://pub.flutter-io.cn" |
753 | source: hosted | 767 | source: hosted |
754 | - version: "1.8.0" | 768 | + version: "1.8.1" |
755 | path_provider: | 769 | path_provider: |
756 | dependency: "direct main" | 770 | dependency: "direct main" |
757 | description: | 771 | description: |
... | @@ -906,13 +920,6 @@ packages: | ... | @@ -906,13 +920,6 @@ packages: |
906 | url: "https://pub.flutter-io.cn" | 920 | url: "https://pub.flutter-io.cn" |
907 | source: hosted | 921 | source: hosted |
908 | version: "1.2.0" | 922 | version: "1.2.0" |
909 | - qr_code_scanner: | ||
910 | - dependency: "direct main" | ||
911 | - description: | ||
912 | - name: qr_code_scanner | ||
913 | - url: "https://pub.flutter-io.cn" | ||
914 | - source: hosted | ||
915 | - version: "0.7.0" | ||
916 | quick_actions: | 923 | quick_actions: |
917 | dependency: "direct main" | 924 | dependency: "direct main" |
918 | description: | 925 | description: |
... | @@ -1120,7 +1127,7 @@ packages: | ... | @@ -1120,7 +1127,7 @@ packages: |
1120 | name: source_span | 1127 | name: source_span |
1121 | url: "https://pub.flutter-io.cn" | 1128 | url: "https://pub.flutter-io.cn" |
1122 | source: hosted | 1129 | source: hosted |
1123 | - version: "1.8.1" | 1130 | + version: "1.8.2" |
1124 | sp_util: | 1131 | sp_util: |
1125 | dependency: transitive | 1132 | dependency: transitive |
1126 | description: | 1133 | description: |
... | @@ -1162,7 +1169,7 @@ packages: | ... | @@ -1162,7 +1169,7 @@ packages: |
1162 | name: sticky_headers | 1169 | name: sticky_headers |
1163 | url: "https://pub.flutter-io.cn" | 1170 | url: "https://pub.flutter-io.cn" |
1164 | source: hosted | 1171 | source: hosted |
1165 | - version: "0.2.0" | 1172 | + version: "0.3.0+2" |
1166 | stream_channel: | 1173 | stream_channel: |
1167 | dependency: transitive | 1174 | dependency: transitive |
1168 | description: | 1175 | description: |
... | @@ -1218,21 +1225,21 @@ packages: | ... | @@ -1218,21 +1225,21 @@ packages: |
1218 | name: test | 1225 | name: test |
1219 | url: "https://pub.flutter-io.cn" | 1226 | url: "https://pub.flutter-io.cn" |
1220 | source: hosted | 1227 | source: hosted |
1221 | - version: "1.19.5" | 1228 | + version: "1.20.2" |
1222 | test_api: | 1229 | test_api: |
1223 | dependency: transitive | 1230 | dependency: transitive |
1224 | description: | 1231 | description: |
1225 | name: test_api | 1232 | name: test_api |
1226 | url: "https://pub.flutter-io.cn" | 1233 | url: "https://pub.flutter-io.cn" |
1227 | source: hosted | 1234 | source: hosted |
1228 | - version: "0.4.8" | 1235 | + version: "0.4.9" |
1229 | test_core: | 1236 | test_core: |
1230 | dependency: transitive | 1237 | dependency: transitive |
1231 | description: | 1238 | description: |
1232 | name: test_core | 1239 | name: test_core |
1233 | url: "https://pub.flutter-io.cn" | 1240 | url: "https://pub.flutter-io.cn" |
1234 | source: hosted | 1241 | source: hosted |
1235 | - version: "0.4.9" | 1242 | + version: "0.4.11" |
1236 | timing: | 1243 | timing: |
1237 | dependency: transitive | 1244 | dependency: transitive |
1238 | description: | 1245 | description: |
... | @@ -1330,7 +1337,7 @@ packages: | ... | @@ -1330,7 +1337,7 @@ packages: |
1330 | name: vector_math | 1337 | name: vector_math |
1331 | url: "https://pub.flutter-io.cn" | 1338 | url: "https://pub.flutter-io.cn" |
1332 | source: hosted | 1339 | source: hosted |
1333 | - version: "2.1.1" | 1340 | + version: "2.1.2" |
1334 | vibration: | 1341 | vibration: |
1335 | dependency: "direct main" | 1342 | dependency: "direct main" |
1336 | description: | 1343 | description: |
... | @@ -1386,7 +1393,7 @@ packages: | ... | @@ -1386,7 +1393,7 @@ packages: |
1386 | name: vm_service | 1393 | name: vm_service |
1387 | url: "https://pub.flutter-io.cn" | 1394 | url: "https://pub.flutter-io.cn" |
1388 | source: hosted | 1395 | source: hosted |
1389 | - version: "7.5.0" | 1396 | + version: "8.2.2" |
1390 | watcher: | 1397 | watcher: |
1391 | dependency: transitive | 1398 | dependency: transitive |
1392 | description: | 1399 | description: |
... | @@ -1472,5 +1479,5 @@ packages: | ... | @@ -1472,5 +1479,5 @@ packages: |
1472 | source: hosted | 1479 | source: hosted |
1473 | version: "3.1.0" | 1480 | version: "3.1.0" |
1474 | sdks: | 1481 | sdks: |
1475 | - dart: ">=2.16.2 <3.0.0" | 1482 | + dart: ">=2.17.0 <3.0.0" |
1476 | - flutter: ">=2.10.0" | 1483 | + flutter: ">=3.0.0" | ... | ... |
... | @@ -59,9 +59,9 @@ dependencies: | ... | @@ -59,9 +59,9 @@ dependencies: |
59 | # WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter | 59 | # WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter |
60 | webview_flutter: ^3.0.1 | 60 | webview_flutter: ^3.0.1 |
61 | # 处理键盘事件 https://github.com/diegoveloper/flutter_keyboard_actions | 61 | # 处理键盘事件 https://github.com/diegoveloper/flutter_keyboard_actions |
62 | - keyboard_actions: ^3.4.4 | 62 | + keyboard_actions: ^4.0.0 |
63 | # 列表悬浮头 https://github.com/fluttercommunity/flutter_sticky_headers | 63 | # 列表悬浮头 https://github.com/fluttercommunity/flutter_sticky_headers |
64 | - sticky_headers: ^0.2.0 | 64 | + sticky_headers: ^0.3.0+2 |
65 | # 路由框架 https://github.com/theyakka/fluro | 65 | # 路由框架 https://github.com/theyakka/fluro |
66 | fluro: ^2.0.3 | 66 | fluro: ^2.0.3 |
67 | # 图片缓存 https://github.com/renefloor/flutter_cached_network_image | 67 | # 图片缓存 https://github.com/renefloor/flutter_cached_network_image |
... | @@ -70,8 +70,6 @@ dependencies: | ... | @@ -70,8 +70,6 @@ dependencies: |
70 | sprintf: ^6.0.0 | 70 | sprintf: ^6.0.0 |
71 | # 状态管理 https://github.com/rrousselGit/provider | 71 | # 状态管理 https://github.com/rrousselGit/provider |
72 | provider: ^6.0.2 | 72 | provider: ^6.0.2 |
73 | - # 扫码 https://github.com/juliuscanute/qr_code_scanner | ||
74 | - qr_code_scanner: ^0.7.0 | ||
75 | # App Shortcuts https://github.com/flutter/plugins/tree/master/packages/quick_actions | 73 | # App Shortcuts https://github.com/flutter/plugins/tree/master/packages/quick_actions |
76 | quick_actions: ^0.6.0+8 | 74 | quick_actions: ^0.6.0+8 |
77 | # 振动(支持Web) https://github.com/benjamindean/flutter_vibration | 75 | # 振动(支持Web) https://github.com/benjamindean/flutter_vibration |
... | @@ -113,6 +111,8 @@ dependencies: | ... | @@ -113,6 +111,8 @@ dependencies: |
113 | sign_in_with_apple: ^3.3.0 | 111 | sign_in_with_apple: ^3.3.0 |
114 | event_bus: ^2.0.0 | 112 | event_bus: ^2.0.0 |
115 | 113 | ||
114 | + animated_radial_menu: ^0.0.1 | ||
115 | + | ||
116 | dependency_overrides: | 116 | dependency_overrides: |
117 | decimal: 1.5.0 | 117 | decimal: 1.5.0 |
118 | 118 | ||
... | @@ -135,7 +135,7 @@ dev_dependencies: | ... | @@ -135,7 +135,7 @@ dev_dependencies: |
135 | # activated in the `analysis_options.yaml` file located at the root of your | 135 | # activated in the `analysis_options.yaml` file located at the root of your |
136 | # package. See that file for information about deactivating specific lint | 136 | # package. See that file for information about deactivating specific lint |
137 | # rules and activating additional ones. | 137 | # rules and activating additional ones. |
138 | - flutter_lints: ^1.0.0 | 138 | + flutter_lints: ^2.0.1 |
139 | 139 | ||
140 | json_serializable: ^6.1.3 | 140 | json_serializable: ^6.1.3 |
141 | build_runner: ^2.1.7 | 141 | build_runner: ^2.1.7 | ... | ... |
-
Please register or login to post a comment