优化拖拽浮钮

This commit is contained in:
toly
2020-11-17 21:22:35 +08:00
parent ab88857b47
commit 8f8e51907e
3 changed files with 273 additions and 103 deletions

View File

@@ -0,0 +1,116 @@
import 'dart:math';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/17
/// contact me by email 1981462002@qq.com
/// 说明:
class BurstFlow extends StatefulWidget {
final List<Widget> children;
final Widget menu;
final double startAngle;
final double swapAngle;
BurstFlow({Key key,@required this.children,
this.startAngle = 30 + 90.0,
this.swapAngle = 120,
@required this.menu}) : super(key: key);
@override
BurstFlowState createState() => BurstFlowState();
}
class BurstFlowState extends State<BurstFlow>
with SingleTickerProviderStateMixin {
AnimationController _controller;
bool _closed = true;
@override
void initState() {
super.initState();
_controller =
AnimationController(duration: Duration(milliseconds: 300), vsync: this)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Flow(
delegate: _CircleFlowDelegate(_controller,
swapAngle: widget.swapAngle, startAngle: widget.startAngle),
children: [
...widget.children,
GestureDetector(onTap: toggle, child: widget.menu)
],
);
}
toggle() {
if (_closed) {
_controller.forward();
} else {
_controller.reverse();
}
_closed = !_closed;
}
}
class _CircleFlowDelegate extends FlowDelegate {
final Animation<double> repaint;
_CircleFlowDelegate(this.repaint,
{this.startAngle = 30 + 90.0, this.swapAngle = 120})
: super(repaint: repaint);
final double startAngle;
final double swapAngle;
@override //绘制孩子的方法
void paintChildren(FlowPaintingContext context) {
double radius = context.size.shortestSide / 2;
if (repaint.value > 0.3) {
var count = context.childCount - 1;
var perRad = swapAngle / 180 * pi / (count - 1);
double rotate = startAngle / 180 * pi;
for (int i = 0; i < count; i++) {
var cSizeX = context.getChildSize(i).width / 2;
var cSizeY = context.getChildSize(i).height / 2;
var offsetX =
repaint.value * (radius - cSizeX) * cos(i * perRad + rotate) +
radius;
var offsetY =
repaint.value * (radius - cSizeY) * sin(i * perRad + rotate) +
radius;
context.paintChild(i,
transform: Matrix4.translationValues(
offsetX - cSizeX, offsetY - cSizeY, 0.0),
opacity: repaint.value);
}
}
context.paintChild(context.childCount - 1,
transform: Matrix4.translationValues(
radius - context.getChildSize(context.childCount - 1).width / 2,
radius - context.getChildSize(context.childCount - 1).height / 2,
0.0));
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return true;
}
}

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/9/3
/// contact me by email 1981462002@qq.com
/// 说明:
/// 说明:
class WrapColor extends StatelessWidget {
final Widget child;
@@ -12,10 +12,10 @@ class WrapColor extends StatelessWidget {
WrapColor(
{this.child,
this.color = Colors.blue,
this.radius = 5,
this.padding =
const EdgeInsets.only(left: 4, right: 4, top: 0, bottom: 0)});
this.color = Colors.blue,
this.radius = 5,
this.padding =
const EdgeInsets.only(left: 4, right: 4, top: 0, bottom: 0)});
@override
Widget build(BuildContext context) {
@@ -27,4 +27,24 @@ class WrapColor extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(radius))),
);
}
}
}
class Circled extends StatelessWidget {
final Widget child;
final Color color;
final double radius;
Circled({this.child, this.color = Colors.blue, this.radius = 15});
@override
Widget build(BuildContext context) {
return Container(
width: radius * 2,
height: radius * 2,
child: child,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.all(Radius.circular(radius))),
);
}
}

View File

@@ -7,6 +7,9 @@ import 'package:flutter_unit/components/permanent/circle.dart';
import 'package:flutter_unit/components/permanent/feedback_widget.dart';
import 'package:flutter_unit/views/pages/gallery/picture_frame.dart';
import 'burst_flow.dart';
import 'color_wrapper.dart';
/// create by 张风捷特烈 on 2020/10/21
/// contact me by email 1981462002@qq.com
/// 说明:
@@ -56,8 +59,8 @@ class OverlayToolWrapperState extends State<OverlayToolWrapper>
super.initState();
WidgetsBinding.instance.addPostFrameCallback((callback) {
var px = MediaQuery.of(context).size.width - (outWidth);
var py = 120.0;
var px = MediaQuery.of(context).size.width - 100;
var py = 40.0;
offset = Offset(px, py);
_ctrl = AnimationController(
@@ -86,112 +89,143 @@ class OverlayToolWrapperState extends State<OverlayToolWrapper>
entry.markNeedsBuild();
}
///绘制悬浮控件
_buildFloating() => Material(
color: Colors.transparent,
child: Row(
children: [
GestureDetector(
onTap: () async {
if (out) {
close();
} else {
open();
}
},
onPanUpdate: (DragUpdateDetails details) {
// offset = offset + details.delta;
double y = details.globalPosition.dy;
double x = details.globalPosition.dx;
if (y < 50) {
y = 50;
}
var px = MediaQuery.of(context).size.width - (outWidth);
final double circleRadius = 80;
final double menuSize = 36;
if (x < px - (width - outWidth)) {
x = px - (width - outWidth);
out = true;
}
GlobalKey<BurstFlowState> burstFlowKey = GlobalKey<BurstFlowState>();
if (x > px) {
out = false;
_buildFloating() {
Color wrapColor = Colors.blue.withOpacity(0.6);
x = px;
}
bool left = offset.dx < 100;
print('-----left----${offset.dx}----');
if (y > MediaQuery.of(context).size.height - 50) {
y = MediaQuery.of(context).size.height - 50;
}
return Container(
width: circleRadius * 2,
height: circleRadius * 2,
alignment: Alignment.center,
child: IconTheme(
data: IconTheme.of(context).copyWith(color: Colors.white, size: 18),
child: BurstFlow(
key:burstFlowKey,
startAngle: !left ? 90.0 + 15 : -90 + 15.0,
swapAngle: !left ? 180.0 - 15 * 2 : 180.0 - 15 * 2.0,
menu: GestureDetector(
onPanEnd: (details) {
double y = offset.dy;
double x = offset.dx;
if (offset.dx >
MediaQuery.of(context).size.width / 2 - circleRadius) {
x = MediaQuery.of(context).size.width -
menuSize / 2 -
circleRadius;
} else {
x = menuSize / 2 - circleRadius;
}
offset = Offset(x, y);
entry.markNeedsBuild();
},
onPanUpdate: (DragUpdateDetails details) {
double y = details.globalPosition.dy - circleRadius;
double x = details.globalPosition.dx - circleRadius;
if (x < menuSize / 2 - circleRadius) {
x = menuSize / 2 - circleRadius;
}
if (y < menuSize / 2 - circleRadius) {
y = menuSize / 2 - circleRadius;
}
if (x >
MediaQuery.of(context).size.width -
menuSize / 2 -
circleRadius) {
x = MediaQuery.of(context).size.width -
menuSize / 2 -
circleRadius;
}
if (y >
MediaQuery.of(context).size.height -
menuSize / 2 -
circleRadius) {
y = MediaQuery.of(context).size.height -
menuSize / 2 -
circleRadius;
}
offset = Offset(x, y);
entry.markNeedsBuild();
},
child: Opacity(
opacity: 0.9,
child: Container(
width: menuSize,
height: menuSize,
padding: EdgeInsets.all(1.5),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(menuSize / 2)),
offset = Offset(x, y - boxHeight / 2);
entry.markNeedsBuild();
},
child: Opacity(
opacity: 0.7,
child: Container(
width: outWidth,
height: outWidth,
padding: EdgeInsets.all(4),
child: Image.asset('assets/images/icon_head.webp'),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
boxShadow: [
BoxShadow(
color:
Theme.of(context).primaryColor.withAlpha(128),
offset: Offset(.5, .5),
spreadRadius: .5,
blurRadius: .5)
],
borderRadius:
BorderRadius.all(Radius.circular(outWidth / 2))),
color: Colors.blue,
image: DecorationImage(
image: AssetImage('assets/images/icon_head.webp')),
borderRadius: BorderRadius.circular(menuSize / 2)),
),
)),
PictureFrame(
alignment: Alignment.center,
marge: EdgeInsets.only(left: 8),
height: boxHeight,
width: width - outWidth + 15,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
spacing: 10,
runSpacing: 10,
children: [
buildItem(TolyIcon.icon_bug, () {
BlocProvider.of<PointBloc>(context).add(EventLoadPoint());
Navigator.of(context).pushNamed(UnitRouter.point);
}),
buildItem(Icons.palette, () {
Navigator.of(context).pushNamed(UnitRouter.galley);
}),
buildItem(Icons.widgets, () {
// Navigator.of(context).pushNamed(UnitRouter.galley);
}),
buildItem(TolyIcon.icon_tag, () {
// Navigator.of(context).pushNamed(UnitRouter.galley);
}),
buildItem(Icons.arrow_forward_outlined, () {
Scaffold.of(context).openDrawer();
}),
buildItem(Icons.settings, () {
Navigator.of(context).pushNamed(UnitRouter.setting);
}),
buildItem(Icons.arrow_back, () {
Scaffold.of(context).openEndDrawer();
}),
buildItem(Icons.close, () {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
}),
],
),
),
),
],
),
);
children: [
FeedbackWidget(
onPressed: () {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
burstFlowKey.currentState.toggle();
},
child: Circled(color: wrapColor, child: Icon(Icons.close))),
FeedbackWidget(
onPressed: () {
BlocProvider.of<PointBloc>(context).add(EventLoadPoint());
Navigator.of(context).pushNamed(UnitRouter.point);
burstFlowKey.currentState.toggle();
},
child: Circled(
color: wrapColor,
radius: 15,
child: Icon(TolyIcon.icon_bug))),
FeedbackWidget(
onPressed: () {
Navigator.of(context).pushNamed(UnitRouter.galley);
burstFlowKey.currentState.toggle();
},
child: Circled(
color: wrapColor,
radius: 15,
child: Icon(Icons.palette))),
FeedbackWidget(
onPressed: () {
burstFlowKey.currentState.toggle();
},
child: Circled(color: wrapColor, child: Icon(Icons.widgets))),
FeedbackWidget(
onPressed: () {
Navigator.of(context).pushNamed(UnitRouter.setting);
burstFlowKey.currentState.toggle();
},
child: Circled(color: wrapColor, child: Icon(Icons.settings))),
]),
),
);
}
Widget buildItem(IconData icon, Function onPress) {
return FeedbackWidget(