reason

修改框架适配首页逻辑

......@@ -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(
......
......@@ -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();
......
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(), // 禁止滑动
......
......@@ -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();
......
......@@ -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 {
......
......@@ -85,7 +85,7 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
void initState() {
super.initState();
// 获取Build完成状态监听
WidgetsBinding.instance!.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_showHint();
});
}
......
......@@ -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
......
......@@ -28,7 +28,7 @@ class LangSortBottomSheetState extends State<LangSortBottomSheet>
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.provider.initData();
});
}
......
......@@ -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
......
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;
......
library flutter_radial_menu;
export 'src/radial_menu.dart';
export 'src/radial_menu_item.dart';
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;
}
}
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;
}
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,
),
),
);
}
}
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,
),
);
}
}
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;
}
}
......@@ -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 = '';
});
},
......
......@@ -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.15.0"
version: "1.16.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.16.2 <3.0.0"
flutter: ">=2.10.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"
......
......@@ -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
......