Toggle navigation
Toggle navigation
This project
Loading...
Sign in
OnePoem
/
OnePoem-App
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
reason
2022-05-16 18:53:24 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
3f1fab8a5b84b83fee369f2e6224dddea6e7d515
3f1fab8a
1 parent
24a1b90b
修改框架适配首页逻辑
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
215 additions
and
847 deletions
lib/account/page/account_page.dart
lib/common/permission_request_widget.dart
lib/home/home_page.dart
lib/home/splash_page.dart
lib/login/page/login_page.dart
lib/poem/page/poem_detail.dart
lib/poem/page/poem_page.dart
lib/poem/widgets/lang_sort_bottom_sheet.dart
lib/setting/page/about_page.dart
lib/tiktok/widgets/tiktok_scaffold.dart
lib/widgets/radial/flutter_radial_menu.dart
lib/widgets/radial/src/arc_progress_indicator.dart
lib/widgets/radial/src/radial_menu.dart
lib/widgets/radial/src/radial_menu_button.dart
lib/widgets/radial/src/radial_menu_center_button.dart
lib/widgets/radial/src/radial_menu_item.dart
lib/widgets/search_bar.dart
pubspec.lock
pubspec.yaml
lib/account/page/account_page.dart
View file @
3f1fab8
...
...
@@ -3,6 +3,7 @@ import 'package:Parlando/account/view_models/account_view_model.dart';
import
'package:Parlando/apis/api_response.dart'
;
import
'package:Parlando/login/login_router.dart'
;
import
'package:Parlando/res/constant.dart'
;
import
'package:Parlando/routers/routers.dart'
;
import
'package:cached_network_image/cached_network_image.dart'
;
import
'package:flustars/flustars.dart'
;
import
'package:flutter/material.dart'
;
...
...
@@ -121,6 +122,8 @@ class _AccountPageState extends State<AccountPage> {
likeButton
,
avatar
,
Positioned
(
left:
64
.
px
,
bottom:
10
.
px
,
child:
GestureDetector
(
child:
CircleAvatar
(
backgroundColor:
Colors
.
red
.
withOpacity
(
0.4
),
...
...
@@ -136,8 +139,6 @@ class _AccountPageState extends State<AccountPage> {
context
,
AccountRouter
.
accountEditPage
);
},
),
left:
64
.
px
,
bottom:
10
.
px
,
),
],
),
...
...
@@ -249,6 +250,51 @@ class _AccountPageState extends State<AccountPage> {
],
),
),
bottomNavigationBar:
Consumer
(
builder:
(
_
,
provider
,
__
)
{
return
BottomAppBar
(
color:
Colors
.
grey
,
child:
Row
(
mainAxisSize:
MainAxisSize
.
max
,
mainAxisAlignment:
MainAxisAlignment
.
spaceAround
,
children:
<
Widget
>[
InkWell
(
onTap:
()
{
NavigatorUtils
.
push
(
context
,
Routes
.
home
,
clearStack:
true
);
},
child:
Container
(
alignment:
Alignment
.
center
,
height:
36.0
,
child:
Text
(
ParlandoLocalizations
.
of
(
context
)
.
onePoemBottomNavigationBarItemTitle
,
style:
const
TextStyle
(
color:
Colors
.
white54
,
fontSize:
15.0
,
),
),
),
),
InkWell
(
onTap:
()
{},
child:
Container
(
alignment:
Alignment
.
center
,
height:
36.0
,
child:
Text
(
ParlandoLocalizations
.
of
(
context
)
.
profileBottomNavigationBarItemTitle
,
style:
const
TextStyle
(
color:
Colors
.
white54
,
fontSize:
15.0
,
),
),
),
),
]),
);
},
),
);
case
Status
.
ERROR
:
return
Center
(
...
...
lib/common/permission_request_widget.dart
View file @
3f1fab8
...
...
@@ -41,7 +41,7 @@ class _PermissionRequestWidgetState extends State<PermissionRequestWidget>
checkPermission
();
//注册观察者
if
(
WidgetsBinding
.
instance
!=
null
)
{
WidgetsBinding
.
instance
!
.
addObserver
(
this
);
WidgetsBinding
.
instance
.
addObserver
(
this
);
}
}
...
...
@@ -164,7 +164,7 @@ class _PermissionRequestWidgetState extends State<PermissionRequestWidget>
void
dispose
()
{
//注销观察者
if
(
WidgetsBinding
.
instance
!=
null
)
{
WidgetsBinding
.
instance
!
.
removeObserver
(
this
);
WidgetsBinding
.
instance
.
removeObserver
(
this
);
}
super
.
dispose
();
...
...
lib/home/home_page.dart
View file @
3f1fab8
import
'package:Parlando/events/trans_event.dart'
;
import
'package:Parlando/poem/poem_router.dart'
;
import
'package:Parlando/common/permission_request_widget.dart'
;
import
'package:Parlando/routers/fluro_navigator.dart'
;
import
'package:Parlando/widgets/radial/flutter_radial_menu.dart'
;
import
'package:flutter/material.dart'
;
import
'package:Parlando/account/page/account_page.dart'
;
import
'package:Parlando/poem/page/poem_page.dart'
;
import
'package:Parlando/widgets/double_tap_back_exit_app.dart'
;
import
'package:provider/provider.dart'
;
import
'package:flutter_gen/gen_l10n/Parlando_localizations.dart'
;
import
'provider/home_provider.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
class
Home
extends
StatefulWidget
{
const
Home
({
Key
?
key
})
:
super
(
key:
key
);
...
...
@@ -17,56 +15,42 @@ class Home extends StatefulWidget {
_HomeState
createState
()
=>
_HomeState
();
}
enum
MenuOptions
{
audio
,
video
,
}
class
_HomeState
extends
State
<
Home
>
with
RestorationMixin
{
late
List
<
Widget
>
_pageList
;
final
PageController
_pageController
=
PageController
();
HomeProvider
provider
=
HomeProvider
();
final
GlobalKey
<
RadialMenuState
>
_menuKey
=
GlobalKey
<
RadialMenuState
>();
final
List
<
RadialMenuItem
<
MenuOptions
>>
items
=
<
RadialMenuItem
<
MenuOptions
>>[
const
RadialMenuItem
<
MenuOptions
>(
tooltip:
'audio'
,
value:
MenuOptions
.
audio
,
child:
Icon
(
Icons
.
mic_none_outlined
,
),
iconColor:
Colors
.
white
,
backgroundColor:
Colors
.
blue
,
),
const
RadialMenuItem
<
MenuOptions
>(
tooltip:
"video"
,
value:
MenuOptions
.
video
,
child:
Icon
(
Icons
.
video_call_outlined
,
),
iconColor:
Colors
.
white
,
backgroundColor:
Colors
.
green
,
),
];
void
_onItemSelected
(
MenuOptions
value
)
{
if
(
value
==
MenuOptions
.
video
)
{
NavigatorUtils
.
push
(
context
,
'
${PoemRouter.poemRecordAudioPage}
?id=100'
,
);
}
else
if
(
value
==
MenuOptions
.
audio
)
{
NavigatorUtils
.
push
(
context
,
'
${PoemRouter.poemRecordVideoPage}
?data=100'
,
);
}
}
@override
void
initState
()
{
super
.
initState
();
initData
();
List
<
String
>
list
=
[
"为您更好的体验应用,所以需要获取您的手机摄像头权限,以保存您的一些偏好设置"
,
"您已拒绝权限,所以无法保存您的一些偏好设置,将无法使用APP"
,
"您已拒绝权限,请在设置中心中同意APP的权限请求"
,
"其他错误"
];
Future
.
delayed
(
Duration
.
zero
,
()
{
NavigatorUtils
.
pushPageByFade
(
context:
context
,
//目标页面
targetPage:
PermissionRequestWidget
(
//所需要申请的权限
permission:
Permission
.
camera
,
//显示关闭应用按钮
isCloseApp:
true
,
//提示文案
permissionList:
list
,
),
//权限申请结果
dismissCallBack:
(
value
)
{},
);
},
);
}
@override
...
...
@@ -88,87 +72,6 @@ class _HomeState extends State<Home> with RestorationMixin {
create:
(
_
)
=>
provider
,
child:
DoubleTapBackExitApp
(
child:
Scaffold
(
floatingActionButton:
FloatingActionButton
(
onPressed:
()
{
eventBus
.
fire
(
TransEvent
());
NavigatorUtils
.
push
(
context
,
'
${PoemRouter.poemRecordVideoPage}
?data=100'
,
);
},
tooltip:
"发一言"
,
backgroundColor:
Colors
.
white
,
child:
const
Icon
(
Icons
.
video_call_outlined
,
color:
Colors
.
black45
,
),
),
// floatingActionButton: SizedBox(
// height: 60,
// child: RadialMenu(
// key: _menuKey,
// items: items,
// radius: 80.0,
// onSelected: _onItemSelected,
// progressAnimationDuration:const Duration(milliseconds: 1),
// ),
// ),
floatingActionButtonLocation:
FloatingActionButtonLocation
.
centerDocked
,
bottomNavigationBar:
Consumer
<
HomeProvider
>(
builder:
(
_
,
provider
,
__
)
{
return
BottomAppBar
(
color:
Colors
.
black45
,
child:
Row
(
mainAxisSize:
MainAxisSize
.
max
,
mainAxisAlignment:
MainAxisAlignment
.
spaceAround
,
children:
<
Widget
>[
InkWell
(
onTap:
()
{
_pageController
.
animateToPage
(
0
,
duration:
const
Duration
(
milliseconds:
100
),
curve:
Curves
.
easeOutSine
,
);
},
child:
Container
(
alignment:
Alignment
.
center
,
height:
36.0
,
child:
Text
(
ParlandoLocalizations
.
of
(
context
)
.
onePoemBottomNavigationBarItemTitle
,
style:
const
TextStyle
(
color:
Colors
.
white54
,
fontSize:
15.0
,
),
),
),
),
InkWell
(
onTap:
()
{
_pageController
.
animateToPage
(
1
,
duration:
const
Duration
(
milliseconds:
100
),
curve:
Curves
.
easeOutSine
,
);
},
child:
Container
(
alignment:
Alignment
.
center
,
height:
36.0
,
child:
Text
(
ParlandoLocalizations
.
of
(
context
)
.
profileBottomNavigationBarItemTitle
,
style:
const
TextStyle
(
color:
Colors
.
white54
,
fontSize:
15.0
,
),
),
),
),
]),
);
},
),
// 使用PageView的原因参看 https://zhuanlan.zhihu.com/p/58582876
body:
PageView
(
physics:
const
NeverScrollableScrollPhysics
(),
// 禁止滑动
...
...
lib/home/splash_page.dart
View file @
3f1fab8
...
...
@@ -34,7 +34,7 @@ class _SplashPageState extends State<SplashPage> {
@override
void
initState
()
{
super
.
initState
();
WidgetsBinding
.
instance
!
.
addPostFrameCallback
((
_
)
async
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
async
{
/// 两种初始化方案,另一种见 main.dart
/// 两种方法各有优劣
await
SpUtil
.
getInstance
();
...
...
lib/login/page/login_page.dart
View file @
3f1fab8
...
...
@@ -8,7 +8,6 @@ import 'package:flustars/flustars.dart';
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:Parlando/common/permission_request_widget.dart'
;
import
'package:Parlando/common/protocol_model.dart'
;
import
'package:Parlando/home/webview_page.dart'
;
import
'package:Parlando/login/widgets/my_text_field.dart'
;
...
...
@@ -29,8 +28,6 @@ import '../login_router.dart';
import
'package:flutter_gen/gen_l10n/Parlando_localizations.dart'
;
import
'package:Parlando/extension/int_extension.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
/// design/1注册登录/index.html
class
LoginPage
extends
StatefulWidget
{
const
LoginPage
({
Key
?
key
})
:
super
(
key:
key
);
...
...
@@ -63,41 +60,13 @@ class _LoginPageState extends State<LoginPage>
@override
void
initState
()
{
super
.
initState
();
WidgetsBinding
.
instance
!
.
addPostFrameCallback
((
_
)
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
/// 显示状态栏和导航栏
SystemChrome
.
setEnabledSystemUIMode
(
SystemUiMode
.
manual
,
overlays:
[
SystemUiOverlay
.
top
,
SystemUiOverlay
.
bottom
]);
});
_nameController
.
text
=
SpUtil
.
getString
(
Constant
.
phone
).
nullSafe
;
List
<
String
>
_list
=
[
"为您更好的体验应用,所以需要获取您的手机文件存储权限,以保存您的一些偏好设置"
,
"您已拒绝权限,所以无法保存您的一些偏好设置,将无法使用APP"
,
"您已拒绝权限,请在设置中心中同意APP的权限请求"
,
"其他错误"
];
// Future.delayed(
// Duration.zero,
// () {
// NavigatorUtils.pushPageByFade(
// context: context,
// //目标页面
// targetPage: PermissionRequestWidget(
// //所需要申请的权限
// permission: Permission.camera,
// //显示关闭应用按钮
// isCloseApp: true,
// //提示文案
// permissionList: _list,
// ),
// //权限申请结果
// dismissCallBack: (value) {
// showPrivacyPage();
// },
// );
// },
// );
showPrivacyPage
();
}
void
showPrivacyPage
()
async
{
...
...
lib/poem/page/poem_detail.dart
View file @
3f1fab8
...
...
@@ -85,7 +85,7 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
void
initState
()
{
super
.
initState
();
// 获取Build完成状态监听
WidgetsBinding
.
instance
!
.
addPostFrameCallback
((
_
)
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
_showHint
();
});
}
...
...
lib/poem/page/poem_page.dart
View file @
3f1fab8
...
...
@@ -48,7 +48,7 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver {
@override
void
dispose
()
{
WidgetsBinding
.
instance
!
.
removeObserver
(
this
);
WidgetsBinding
.
instance
.
removeObserver
(
this
);
_videoListController
.
currentPlayer
.
pause
();
bus
.
cancel
();
super
.
dispose
();
...
...
@@ -57,16 +57,16 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver {
@override
void
initState
()
{
videoDataList
=
UserVideo
.
fetchVideo
();
WidgetsBinding
.
instance
!
.
addObserver
(
this
);
WidgetsBinding
.
instance
.
addObserver
(
this
);
_videoListController
.
init
(
pageController:
_pageController
,
initialList:
videoDataList
.
map
(
(
e
)
=>
VPVideoController
(
videoInfo:
e
,
builder:
()
=>
VideoPlayerController
.
asset
(
e
.
url
),
),
)
videoInfo:
e
,
builder:
()
=>
VideoPlayerController
.
asset
(
e
.
url
),
),
)
.
toList
(),
videoProvider:
(
int
index
,
List
<
VPVideoController
>
list
)
async
{
return
videoDataList
...
...
lib/poem/widgets/lang_sort_bottom_sheet.dart
View file @
3f1fab8
...
...
@@ -28,7 +28,7 @@ class LangSortBottomSheetState extends State<LangSortBottomSheet>
@override
void
initState
()
{
super
.
initState
();
WidgetsBinding
.
instance
!
.
addPostFrameCallback
((
_
)
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
widget
.
provider
.
initData
();
});
}
...
...
lib/setting/page/about_page.dart
View file @
3f1fab8
...
...
@@ -54,7 +54,7 @@ class _AboutPageState extends State<AboutPage> {
@override
void
initState
()
{
super
.
initState
();
WidgetsBinding
.
instance
!
.
addPostFrameCallback
((
_
)
async
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
async
{
// 2s定时器
_countdownTimer
=
Timer
.
periodic
(
const
Duration
(
seconds:
2
),
(
_
)
{
// https://www.jianshu.com/p/e4106b829bff
...
...
lib/tiktok/widgets/tiktok_scaffold.dart
View file @
3f1fab8
import
'dart:math'
;
import
'package:Parlando/account/account_router.dart'
;
import
'package:Parlando/events/trans_event.dart'
;
import
'package:Parlando/home/provider/home_provider.dart'
;
import
'package:Parlando/poem/poem_router.dart'
;
import
'package:Parlando/routers/fluro_navigator.dart'
;
import
'package:animated_radial_menu/animated_radial_menu.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:Parlando/tiktok/style/style.dart'
;
import
'package:provider/provider.dart'
;
import
'package:flutter_gen/gen_l10n/Parlando_localizations.dart'
;
const
double
scrollSpeed
=
300
;
...
...
@@ -185,6 +193,82 @@ class _TikTokScaffoldState extends State<TikTokScaffold>
body:
body
,
backgroundColor:
Colors
.
black
,
resizeToAvoidBottomInset:
false
,
floatingActionButton:
SizedBox
(
height:
60
,
child:
RadialMenu
(
children:
[
RadialButton
(
icon:
const
Icon
(
Icons
.
video_call_outlined
),
buttonColor:
Colors
.
teal
,
onPress:
()
{
eventBus
.
fire
(
TransEvent
());
NavigatorUtils
.
push
(
context
,
'
${PoemRouter.poemRecordVideoPage}
?data=100'
,
);
}),
RadialButton
(
icon:
const
Icon
(
Icons
.
mic_none_outlined
),
buttonColor:
Colors
.
green
,
onPress:
()
{
eventBus
.
fire
(
TransEvent
());
NavigatorUtils
.
push
(
context
,
'
${PoemRouter.poemRecordAudioPage}
?id=100'
,
);
}),
],
),
),
bottomNavigationBar:
Consumer
<
HomeProvider
>(
builder:
(
_
,
provider
,
__
)
{
return
BottomAppBar
(
color:
Colors
.
grey
,
child:
Row
(
mainAxisSize:
MainAxisSize
.
max
,
mainAxisAlignment:
MainAxisAlignment
.
spaceAround
,
children:
<
Widget
>[
InkWell
(
onTap:
()
{},
child:
Container
(
alignment:
Alignment
.
center
,
height:
36.0
,
child:
Text
(
ParlandoLocalizations
.
of
(
context
)
.
onePoemBottomNavigationBarItemTitle
,
style:
const
TextStyle
(
color:
Colors
.
white54
,
fontSize:
15.0
,
),
),
),
),
InkWell
(
onTap:
()
{
eventBus
.
fire
(
TransEvent
());
NavigatorUtils
.
push
(
context
,
AccountRouter
.
accountPage
,
);
},
child:
Container
(
alignment:
Alignment
.
center
,
height:
36.0
,
child:
Text
(
ParlandoLocalizations
.
of
(
context
)
.
profileBottomNavigationBarItemTitle
,
style:
const
TextStyle
(
color:
Colors
.
white54
,
fontSize:
15.0
,
),
),
),
),
]),
);
},
),
floatingActionButtonLocation:
FloatingActionButtonLocation
.
centerDocked
,
),
);
return
body
;
...
...
@@ -344,6 +428,7 @@ class _MiddlePage extends StatelessWidget {
required
this
.
tabBar
,
this
.
page
,
})
:
super
(
key:
key
);
@override
Widget
build
(
BuildContext
context
)
{
Widget
mainVideoList
=
Container
(
...
...
@@ -440,6 +525,7 @@ class _LeftPageTransform extends StatelessWidget {
const
_LeftPageTransform
({
Key
?
key
,
this
.
offsetX
,
this
.
content
})
:
super
(
key:
key
);
@override
Widget
build
(
BuildContext
context
)
{
final
screenWidth
=
MediaQuery
.
of
(
context
).
size
.
width
;
...
...
lib/widgets/radial/flutter_radial_menu.dart
deleted
100644 → 0
View file @
24a1b90
library
flutter_radial_menu
;
export
'src/radial_menu.dart'
;
export
'src/radial_menu_item.dart'
;
lib/widgets/radial/src/arc_progress_indicator.dart
deleted
100644 → 0
View file @
24a1b90
import
'dart:math'
as
Math
;
import
'package:flutter/material.dart'
;
/// Draws an [ActionIcon] and [_ArcProgressPainter] that represent an active action.
/// As the provided [Animation] progresses the ActionArc grows into a full
/// circle and the ActionIcon moves along it.
class
ArcProgressIndicator
extends
StatelessWidget
{
// required
final
Animation
<
double
>
controller
;
final
double
radius
;
// optional
final
double
startAngle
;
final
double
?
width
;
/// The color to use when filling the arc.
///
/// Defaults to the accent color of the current theme.
final
Color
?
color
;
final
IconData
icon
;
final
Color
?
iconColor
;
final
double
?
iconSize
;
// private
final
Animation
<
double
>
_progress
;
ArcProgressIndicator
({
Key
?
key
,
required
this
.
controller
,
required
this
.
radius
,
this
.
startAngle
=
0.0
,
this
.
width
,
this
.
color
,
required
this
.
icon
,
this
.
iconColor
,
this
.
iconSize
,
})
:
_progress
=
Tween
(
begin:
0.0
,
end:
1.0
).
animate
(
controller
),
super
(
key:
key
);
@override
Widget
build
(
BuildContext
context
)
{
late
TextPainter
_iconPainter
;
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
Color
?
_iconColor
=
iconColor
??
theme
.
colorScheme
.
secondary
;
final
double
?
_iconSize
=
iconSize
??
IconTheme
.
of
(
context
).
size
;
_iconPainter
=
TextPainter
(
textDirection:
Directionality
.
of
(
context
),
text:
TextSpan
(
text:
String
.
fromCharCode
(
icon
.
codePoint
),
style:
TextStyle
(
inherit:
false
,
color:
_iconColor
,
fontSize:
_iconSize
,
fontFamily:
icon
.
fontFamily
,
package:
icon
.
fontPackage
,
),
),
)..
layout
();
return
CustomPaint
(
painter:
_ArcProgressPainter
(
controller:
_progress
,
color:
color
??
theme
.
colorScheme
.
secondary
,
radius:
radius
,
width:
width
??
_iconSize
!
*
2
,
startAngle:
startAngle
,
icon:
_iconPainter
,
),
);
}
}
class
_ArcProgressPainter
extends
CustomPainter
{
// required
final
Animation
<
double
>
controller
;
final
Color
color
;
final
double
radius
;
final
double
width
;
// optional
final
double
startAngle
;
final
TextPainter
icon
;
_ArcProgressPainter
({
required
this
.
controller
,
required
this
.
color
,
required
this
.
radius
,
required
this
.
width
,
this
.
startAngle
=
0.0
,
required
this
.
icon
,
})
:
super
(
repaint:
controller
);
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
Paint
paint
=
Paint
()
..
color
=
color
..
strokeWidth
=
width
..
strokeCap
=
StrokeCap
.
round
..
style
=
PaintingStyle
.
stroke
;
final
double
sweepAngle
=
controller
.
value
*
2
*
Math
.
pi
;
canvas
.
drawArc
(
Offset
.
zero
&
size
,
startAngle
,
sweepAngle
,
false
,
paint
,
);
double
angle
=
startAngle
+
sweepAngle
;
Offset
offset
=
Offset
(
(
size
.
width
/
2
-
icon
.
size
.
width
/
2
)
+
radius
*
Math
.
cos
(
angle
),
(
size
.
height
/
2
-
icon
.
size
.
height
/
2
)
+
radius
*
Math
.
sin
(
angle
),
);
icon
.
paint
(
canvas
,
offset
);
}
@override
bool
shouldRepaint
(
_ArcProgressPainter
other
)
{
return
controller
.
value
!=
other
.
controller
.
value
||
color
!=
other
.
color
||
radius
!=
other
.
radius
||
width
!=
other
.
width
||
startAngle
!=
other
.
startAngle
||
icon
!=
other
.
icon
;
}
}
lib/widgets/radial/src/radial_menu.dart
deleted
100644 → 0
View file @
24a1b90
import
'dart:async'
;
import
'dart:math'
as
math
;
import
'package:flutter/material.dart'
;
import
'package:Parlando/widgets/radial/src/radial_menu_button.dart'
;
import
'package:Parlando/widgets/radial/src/radial_menu_center_button.dart'
;
import
'package:Parlando/widgets/radial/src/radial_menu_item.dart'
;
const
double
_radiansPerDegree
=
math
.
pi
/
180
;
const
double
_startAngle
=
-
120.0
*
_radiansPerDegree
;
typedef
ItemAngleCalculator
=
double
Function
(
int
index
);
/// A radial menu for selecting from a list of items.
///
/// A radial menu lets the user select from a number of items. It displays a
/// button that opens the menu, showing its items arranged in an arc. Selecting
/// an item triggers the animation of a progress bar drawn at the specified
/// [radius] around the central menu button.
///
/// The type `T` is the type of the values the radial menu represents. All the
/// entries in a given menu must represent values with consistent types.
/// Typically, an enum is used. Each [RadialMenuItem] in [items] must be
/// specialized with that same type argument.
///
/// Requires one of its ancestors to be a [Material] widget.
///
/// See also:
///
/// * [RadialMenuItem], the widget used to represent the [items].
/// * [RadialMenuCenterButton], the button used to open and close the menu.
class
RadialMenu
<
T
>
extends
StatefulWidget
{
/// Creates a dropdown button.
///
/// The [items] must have distinct values.
///
/// The [radius], [menuAnimationDuration], and [progressAnimationDuration]
/// arguments must not be null (they all have defaults, so do not need to be
/// specified).
const
RadialMenu
({
Key
?
key
,
required
this
.
items
,
required
this
.
onSelected
,
this
.
radius
=
100.0
,
this
.
menuAnimationDuration
=
const
Duration
(
milliseconds:
1000
),
this
.
progressAnimationDuration
=
const
Duration
(
milliseconds:
1000
),
})
:
super
(
key:
key
);
/// The list of possible items to select among.
final
List
<
RadialMenuItem
<
T
>>
items
;
/// Called when the user selects an item.
final
Function
onSelected
;
// TODO why Function? not ValueChanged?
/// The radius of the arc used to lay out the items and draw the progress bar.
///
/// Defaults to 100.0.
final
double
radius
;
/// Duration of the menu opening/closing animation.
///
/// Defaults to 1000 milliseconds.
final
Duration
menuAnimationDuration
;
/// Duration of the action activation progress arc animation.
///
/// Defaults to 1000 milliseconds.
final
Duration
progressAnimationDuration
;
@override
RadialMenuState
createState
()
=>
RadialMenuState
();
}
class
RadialMenuState
extends
State
<
RadialMenu
>
with
TickerProviderStateMixin
{
late
AnimationController
_menuAnimationController
;
late
AnimationController
_progressAnimationController
;
bool
_isOpen
=
false
;
int
_activeItemIndex
=
-
1
;
// todo: xqwzts: allow users to pass in their own calculator as a param.
// and change this to the default: radialItemAngleCalculator.
double
calculateItemAngle
(
int
index
)
{
double
_itemSpacing
=
120.0
/
widget
.
items
.
length
;
return
_startAngle
+
index
*
_itemSpacing
*
_radiansPerDegree
;
}
@override
void
initState
()
{
super
.
initState
();
_menuAnimationController
=
AnimationController
(
duration:
widget
.
menuAnimationDuration
,
vsync:
this
,
);
_progressAnimationController
=
AnimationController
(
duration:
widget
.
progressAnimationDuration
,
vsync:
this
,
);
}
@override
void
dispose
()
{
_menuAnimationController
.
dispose
();
_progressAnimationController
.
dispose
();
super
.
dispose
();
}
void
_openMenu
()
{
_menuAnimationController
.
forward
();
setState
(()
=>
_isOpen
=
true
);
}
void
_closeMenu
()
{
_menuAnimationController
.
reverse
();
setState
(()
=>
_isOpen
=
false
);
}
Future
<
void
>
_activate
(
int
itemIndex
)
async
{
setState
(()
=>
_activeItemIndex
=
itemIndex
);
await
_progressAnimationController
.
forward
().
orCancel
;
widget
.
onSelected
(
widget
.
items
[
itemIndex
].
value
);
_closeMenu
();
}
/// Resets the menu to its initial (closed) state.
void
reset
()
{
_menuAnimationController
.
reset
();
_progressAnimationController
.
reverse
();
setState
(()
{
_isOpen
=
false
;
_activeItemIndex
=
-
1
;
});
}
Widget
_buildActionButton
(
int
index
)
{
final
RadialMenuItem
item
=
widget
.
items
[
index
];
return
LayoutId
(
id:
'
${_RadialMenuLayout.actionButton}$index
'
,
child:
RadialMenuButton
(
child:
item
,
backgroundColor:
item
.
backgroundColor
,
onPressed:
()
=>
_activate
(
index
),
),
);
}
Widget
_buildCenterButton
()
{
return
LayoutId
(
id:
_RadialMenuLayout
.
menuButton
,
child:
RadialMenuCenterButton
(
openCloseAnimationController:
_menuAnimationController
.
view
,
activateAnimationController:
_progressAnimationController
.
view
,
isOpen:
_isOpen
,
onPressed:
_isOpen
?
_closeMenu
:
_openMenu
,
),
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
List
<
Widget
>
children
=
<
Widget
>[];
for
(
int
i
=
0
;
i
<
widget
.
items
.
length
;
i
++)
{
if
(
_activeItemIndex
!=
i
)
{
children
.
add
(
_buildActionButton
(
i
));
}
}
children
.
add
(
_buildCenterButton
());
return
AnimatedBuilder
(
animation:
_menuAnimationController
,
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
CustomMultiChildLayout
(
delegate:
_RadialMenuLayout
(
itemCount:
widget
.
items
.
length
,
radius:
widget
.
radius
,
calculateItemAngle:
calculateItemAngle
,
controller:
_menuAnimationController
.
view
,
),
children:
children
,
);
},
);
}
}
class
_RadialMenuLayout
extends
MultiChildLayoutDelegate
{
static
const
String
menuButton
=
'menuButton'
;
static
const
String
actionButton
=
'actionButton'
;
static
const
String
activeAction
=
'activeAction'
;
final
int
itemCount
;
final
double
radius
;
final
ItemAngleCalculator
calculateItemAngle
;
final
Animation
<
double
>
controller
;
final
Animation
<
double
>
_progress
;
_RadialMenuLayout
({
required
this
.
itemCount
,
required
this
.
radius
,
required
this
.
calculateItemAngle
,
required
this
.
controller
,
})
:
_progress
=
Tween
<
double
>(
begin:
0.0
,
end:
radius
).
animate
(
CurvedAnimation
(
curve:
Curves
.
elasticOut
,
parent:
controller
,
),
);
late
Offset
center
;
@override
void
performLayout
(
Size
size
)
{
center
=
Offset
(
size
.
width
/
2
,
size
.
height
/
2
);
if
(
hasChild
(
menuButton
))
{
Size
menuButtonSize
;
menuButtonSize
=
layoutChild
(
menuButton
,
BoxConstraints
.
loose
(
size
));
// place the menubutton in the center
positionChild
(
menuButton
,
Offset
(
center
.
dx
-
menuButtonSize
.
width
/
2
,
center
.
dy
-
menuButtonSize
.
height
/
2
,
),
);
}
for
(
int
i
=
0
;
i
<
itemCount
;
i
++)
{
final
String
actionButtonId
=
'
$actionButton$i
'
;
final
String
actionArcId
=
'
$activeAction$i
'
;
if
(
hasChild
(
actionArcId
))
{
final
Size
arcSize
=
layoutChild
(
actionArcId
,
BoxConstraints
.
expand
(
width:
_progress
.
value
*
2
,
height:
_progress
.
value
*
2
,
),
);
positionChild
(
actionArcId
,
Offset
(
center
.
dx
-
arcSize
.
width
/
2
,
center
.
dy
-
arcSize
.
height
/
2
,
),
);
}
if
(
hasChild
(
actionButtonId
))
{
final
Size
buttonSize
=
layoutChild
(
actionButtonId
,
BoxConstraints
.
loose
(
size
));
final
double
itemAngle
=
calculateItemAngle
(
i
);
positionChild
(
actionButtonId
,
Offset
(
(
center
.
dx
-
buttonSize
.
width
/
2
)
+
(
_progress
.
value
)
*
math
.
cos
(
itemAngle
),
(
center
.
dy
-
buttonSize
.
height
/
2
)
+
(
_progress
.
value
)
*
math
.
sin
(
itemAngle
),
),
);
}
}
}
@override
bool
shouldRelayout
(
_RadialMenuLayout
oldDelegate
)
=>
itemCount
!=
oldDelegate
.
itemCount
||
radius
!=
oldDelegate
.
radius
||
calculateItemAngle
!=
oldDelegate
.
calculateItemAngle
||
controller
!=
oldDelegate
.
controller
||
_progress
!=
oldDelegate
.
_progress
;
}
lib/widgets/radial/src/radial_menu_button.dart
deleted
100644 → 0
View file @
24a1b90
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
class
RadialMenuButton
extends
StatelessWidget
{
const
RadialMenuButton
({
Key
?
key
,
required
this
.
child
,
required
this
.
backgroundColor
,
required
this
.
onPressed
,
})
:
super
(
key:
key
);
final
Widget
child
;
final
Color
backgroundColor
;
final
VoidCallback
onPressed
;
@override
Widget
build
(
BuildContext
context
)
{
final
Color
color
=
backgroundColor
;
return
Semantics
(
button:
true
,
enabled:
true
,
child:
Material
(
type:
MaterialType
.
circle
,
color:
color
,
child:
InkWell
(
onTap:
onPressed
,
child:
child
,
),
),
);
}
}
lib/widgets/radial/src/radial_menu_center_button.dart
deleted
100644 → 0
View file @
24a1b90
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:Parlando/widgets/radial/src/radial_menu_button.dart'
;
const
double
_defaultButtonSize
=
48.0
;
/// The button at the center of a [RadialMenu] which controls its open/closed
/// state.
class
RadialMenuCenterButton
extends
StatelessWidget
{
/// Drives the opening/closing animation of the [RadialMenu].
final
Animation
<
double
>
openCloseAnimationController
;
/// Drives the animation when an item in the [RadialMenu] is pressed.
final
Animation
<
double
>
activateAnimationController
;
/// Called when the user presses this button.
final
VoidCallback
onPressed
;
/// The opened/closed state of the menu.
///
/// Determines which of [closedColor] or [openedColor] should be used as the
/// background color of the button.
final
bool
isOpen
;
/// The color to use when painting the icon.
///
/// Defaults to [Colors.black].
final
Color
iconColor
;
/// Background color when it is in its closed state.
///
/// Defaults to [Colors.white].
final
Color
closedColor
;
/// Background color when it is in its opened state.
///
/// Defaults to [Colors.grey].
final
Color
openedColor
;
/// The size of the button.
///
/// Defaults to 48.0.
final
double
size
;
/// The animation progress for the [AnimatedIcon] in the center of the button.
final
Animation
<
double
>
_progress
;
/// The scale factor applied to the button.
///
/// Animates from 1.0 to 0.0 when an an item is pressed in the menu and
/// [activateAnimationController] progresses.
final
Animation
<
double
>
_scale
;
RadialMenuCenterButton
({
Key
?
key
,
required
this
.
openCloseAnimationController
,
required
this
.
activateAnimationController
,
required
this
.
onPressed
,
required
this
.
isOpen
,
this
.
iconColor
=
Colors
.
black
,
this
.
closedColor
=
Colors
.
white
,
this
.
openedColor
=
Colors
.
grey
,
this
.
size
=
_defaultButtonSize
,
})
:
_progress
=
Tween
(
begin:
0.0
,
end:
1.0
).
animate
(
CurvedAnimation
(
parent:
openCloseAnimationController
,
curve:
const
Interval
(
0.0
,
0.5
,
curve:
Curves
.
ease
,
),
),
),
_scale
=
Tween
(
begin:
1.0
,
end:
0.0
).
animate
(
CurvedAnimation
(
parent:
activateAnimationController
,
curve:
Curves
.
elasticIn
,
),
),
super
(
key:
key
);
@override
Widget
build
(
BuildContext
context
)
{
final
AnimatedIcon
animatedIcon
=
AnimatedIcon
(
color:
iconColor
,
icon:
AnimatedIcons
.
menu_close
,
progress:
_progress
,
);
final
Widget
child
=
SizedBox
(
width:
size
,
height:
size
,
child:
Center
(
child:
animatedIcon
,
),
);
final
Color
color
=
isOpen
?
openedColor
:
closedColor
;
return
ScaleTransition
(
scale:
_scale
,
child:
RadialMenuButton
(
child:
child
,
backgroundColor:
color
,
onPressed:
onPressed
,
),
);
}
}
lib/widgets/radial/src/radial_menu_item.dart
deleted
100644 → 0
View file @
24a1b90
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
const
double
_defaultButtonSize
=
48.0
;
/// An item in a [RadialMenu].
///
/// The type `T` is the type of the value the entry represents. All the entries
/// in a given menu must represent values with consistent types.
class
RadialMenuItem
<
T
>
extends
StatelessWidget
{
/// Creates a circular action button for an item in a [RadialMenu].
///
/// The [child] argument is required.
const
RadialMenuItem
({
Key
?
key
,
required
this
.
child
,
required
this
.
value
,
required
this
.
tooltip
,
this
.
size
=
_defaultButtonSize
,
required
this
.
backgroundColor
,
required
this
.
iconColor
,
this
.
iconSize
=
24.0
,
})
:
super
(
key:
key
);
/// The widget below this widget in the tree.
///
/// Typically an [Icon] widget.
final
Widget
child
;
/// The value to return if the user selects this menu item.
///
/// Eventually returned in a call to [RadialMenu.onSelected].
final
T
value
;
/// Text that describes the action that will occur when the button is pressed.
///
/// This text is displayed when the user long-presses on the button and is
/// used for accessibility.
final
String
tooltip
;
/// The color to use when filling the button.
///
/// Defaults to the primary color of the current theme.
final
Color
backgroundColor
;
/// The size of the button.
///
/// Defaults to 48.0.
final
double
size
;
/// The color to use when painting the child icon.
///
/// Defaults to the primary icon theme color.
final
Color
?
iconColor
;
final
double
?
iconSize
;
@override
Widget
build
(
BuildContext
context
)
{
final
Color
?
_iconColor
=
iconColor
??
Theme
.
of
(
context
).
primaryIconTheme
.
color
;
late
Widget
result
;
result
=
Center
(
child:
IconTheme
.
merge
(
data:
IconThemeData
(
color:
_iconColor
,
size:
iconSize
,
),
child:
child
,
),
);
result
=
Tooltip
(
message:
tooltip
,
child:
result
,
);
result
=
SizedBox
(
width:
size
,
height:
size
,
child:
result
,
);
return
result
;
}
}
lib/widgets/search_bar.dart
View file @
3f1fab8
...
...
@@ -107,7 +107,7 @@ class _SearchBarState extends State<SearchBar> {
),
onTap:
()
{
/// https://github.com/flutter/flutter/issues/35848
SchedulerBinding
.
instance
!
.
addPostFrameCallback
((
_
)
{
SchedulerBinding
.
instance
.
addPostFrameCallback
((
_
)
{
_controller
.
text
=
''
;
});
},
...
...
pubspec.lock
View file @
3f1fab8
...
...
@@ -15,13 +15,20 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.8.0"
animated_radial_menu:
dependency: "direct main"
description:
name: animated_radial_menu
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.1"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.
6
"
version: "3.1.
11
"
args:
dependency: transitive
description:
...
...
@@ -189,7 +196,7 @@ packages:
name: collection
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1
5
.0"
version: "1.1
6
.0"
common_utils:
dependency: "direct main"
description:
...
...
@@ -210,7 +217,7 @@ packages:
name: coverage
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.
0.3
"
version: "1.
2.0
"
cross_file:
dependency: transitive
description:
...
...
@@ -322,7 +329,7 @@ packages:
name: fake_async
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.
2
.0"
version: "1.
3
.0"
ffi:
dependency: transitive
description:
...
...
@@ -411,7 +418,7 @@ packages:
name: flutter_lints
url: "https://pub.flutter-io.cn"
source: hosted
version: "
1.0.4
"
version: "
2.0.1
"
flutter_localizations:
dependency: "direct main"
description: flutter
...
...
@@ -483,6 +490,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
font_awesome_flutter:
dependency: transitive
description:
name: font_awesome_flutter
url: "https://pub.flutter-io.cn"
source: hosted
version: "9.2.0"
frontend_server_client:
dependency: transitive
description:
...
...
@@ -632,7 +646,7 @@ packages:
name: js
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.
3
"
version: "0.6.
4
"
json_annotation:
dependency: "direct main"
description:
...
...
@@ -653,7 +667,7 @@ packages:
name: keyboard_actions
url: "https://pub.flutter-io.cn"
source: hosted
version: "
3.4.7
"
version: "
4.0.0
"
lint:
dependency: transitive
description:
...
...
@@ -667,7 +681,7 @@ packages:
name: lints
url: "https://pub.flutter-io.cn"
source: hosted
version: "
1.0.1
"
version: "
2.0.0
"
logger:
dependency: transitive
description:
...
...
@@ -695,7 +709,7 @@ packages:
name: material_color_utilities
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.
3
"
version: "0.1.
4
"
meta:
dependency: transitive
description:
...
...
@@ -751,7 +765,7 @@ packages:
name: path
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.
0
"
version: "1.8.
1
"
path_provider:
dependency: "direct main"
description:
...
...
@@ -906,13 +920,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
qr_code_scanner:
dependency: "direct main"
description:
name: qr_code_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.0"
quick_actions:
dependency: "direct main"
description:
...
...
@@ -1120,7 +1127,7 @@ packages:
name: source_span
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.
1
"
version: "1.8.
2
"
sp_util:
dependency: transitive
description:
...
...
@@ -1162,7 +1169,7 @@ packages:
name: sticky_headers
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.
2.0
"
version: "0.
3.0+2
"
stream_channel:
dependency: transitive
description:
...
...
@@ -1218,21 +1225,21 @@ packages:
name: test
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.
19.5
"
version: "1.
20.2
"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.
8
"
version: "0.4.
9
"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.
9
"
version: "0.4.
11
"
timing:
dependency: transitive
description:
...
...
@@ -1330,7 +1337,7 @@ packages:
name: vector_math
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.
1
"
version: "2.1.
2
"
vibration:
dependency: "direct main"
description:
...
...
@@ -1386,7 +1393,7 @@ packages:
name: vm_service
url: "https://pub.flutter-io.cn"
source: hosted
version: "
7.5.0
"
version: "
8.2.2
"
watcher:
dependency: transitive
description:
...
...
@@ -1472,5 +1479,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.1
6.2
<3.0.0"
flutter: ">=
2.1
0.0"
dart: ">=2.1
7.0
<3.0.0"
flutter: ">=
3.
0.0"
...
...
pubspec.yaml
View file @
3f1fab8
...
...
@@ -59,9 +59,9 @@ dependencies:
# WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter
webview_flutter
:
^3.0.1
# 处理键盘事件 https://github.com/diegoveloper/flutter_keyboard_actions
keyboard_actions
:
^
3.4.4
keyboard_actions
:
^
4.0.0
# 列表悬浮头 https://github.com/fluttercommunity/flutter_sticky_headers
sticky_headers
:
^0.
2.0
sticky_headers
:
^0.
3.0+2
# 路由框架 https://github.com/theyakka/fluro
fluro
:
^2.0.3
# 图片缓存 https://github.com/renefloor/flutter_cached_network_image
...
...
@@ -70,8 +70,6 @@ dependencies:
sprintf
:
^6.0.0
# 状态管理 https://github.com/rrousselGit/provider
provider
:
^6.0.2
# 扫码 https://github.com/juliuscanute/qr_code_scanner
qr_code_scanner
:
^0.7.0
# App Shortcuts https://github.com/flutter/plugins/tree/master/packages/quick_actions
quick_actions
:
^0.6.0+8
# 振动(支持Web) https://github.com/benjamindean/flutter_vibration
...
...
@@ -113,6 +111,8 @@ dependencies:
sign_in_with_apple
:
^3.3.0
event_bus
:
^2.0.0
animated_radial_menu
:
^0.0.1
dependency_overrides
:
decimal
:
1.5.0
...
...
@@ -135,7 +135,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints
:
^
1.0.0
flutter_lints
:
^
2.0.1
json_serializable
:
^6.1.3
build_runner
:
^2.1.7
...
...
Please
register
or
login
to post a comment