reason

update

......@@ -51,7 +51,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "pub.yiyan.parlando.Parlando"
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
......@@ -78,5 +78,5 @@ flutter {
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
......
......@@ -10,6 +10,7 @@
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:exported="true"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
......
import 'package:event_bus/event_bus.dart';
EventBus eventBus = EventBus();
class TransEvent {
TransEvent();
}
import 'package:Parlando/events/trans_event.dart';
import 'package:Parlando/poem/poem_router.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';
......@@ -13,13 +17,51 @@ 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,
),
];
List<BottomNavigationBarItem>? _list;
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() {
......@@ -47,21 +89,36 @@ class _HomeState extends State<Home> with RestorationMixin {
child: DoubleTapBackExitApp(
child: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {},
onPressed: () {
eventBus.fire(TransEvent());
NavigatorUtils.push(
context,
'${PoemRouter.poemRecordVideoPage}?data=100',
);
},
tooltip: "发一言",
backgroundColor: Colors.white,
child: const Icon(
Icons.add,
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,
shape: const CircularNotchedRectangle(),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
......@@ -109,7 +166,6 @@ class _HomeState extends State<Home> with RestorationMixin {
),
),
]),
elevation: 5.0,
);
},
),
......
......@@ -8,7 +8,6 @@ import 'package:Parlando/poem/widgets/poem_user_comments.dart';
import 'package:Parlando/res/gaps.dart';
import 'package:Parlando/routers/fluro_navigator.dart';
import 'package:Parlando/util/image_utils.dart';
import 'package:Parlando/widgets/bars/home_action_bar.dart';
import 'package:Parlando/widgets/bars/home_menu_bar.dart';
import 'package:Parlando/widgets/my_app_bar.dart';
......
import 'dart:async';
import 'package:Parlando/events/trans_event.dart';
import 'package:flutter/material.dart';
import 'package:Parlando/category/category_router.dart';
import 'package:Parlando/poem/poem_router.dart';
......@@ -34,6 +37,7 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver {
final TikTokVideoListController _videoListController =
TikTokVideoListController();
List<UserVideo> videoDataList = [];
late StreamSubscription bus;
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
......@@ -46,6 +50,7 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver {
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
_videoListController.currentPlayer.pause();
bus.cancel();
super.dispose();
}
......@@ -87,6 +92,10 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver {
},
);
bus = eventBus.on<TransEvent>().listen((event) {
_videoListController.currentPlayer.pause();
});
super.initState();
}
......
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;
}
}
......@@ -309,6 +309,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
event_bus:
dependency: "direct main"
description:
name: event_bus
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
fake_async:
dependency: transitive
description:
......
......@@ -111,6 +111,7 @@ dependencies:
getwidget: ^2.0.5
sign_in_with_apple: ^3.3.0
event_bus: ^2.0.0
dependency_overrides:
decimal: 1.5.0
......