Files
FlutterUnit/lib/components/permanent/overlay_tool_wrapper.dart
2020-10-27 13:43:30 +08:00

258 lines
7.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_unit/app/res/toly_icon.dart';
import 'package:flutter_unit/app/router.dart';
import 'package:flutter_unit/blocs/bloc_exp.dart';
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';
/// create by 张风捷特烈 on 2020/10/21
/// contact me by email 1981462002@qq.com
/// 说明:
class OverlayToolWrapper extends StatefulWidget {
final Widget child;
OverlayToolWrapper({Key key, this.child}) : super(key: key);
@override
OverlayToolWrapperState createState() => OverlayToolWrapperState();
static OverlayToolWrapperState of(BuildContext context,
{bool nullOk = false}) {
assert(nullOk != null);
assert(context != null);
final OverlayToolWrapperState result =
context.findAncestorStateOfType<OverlayToolWrapperState>();
if (nullOk || result != null) return result;
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary(
'OverlayToolWrapper.of() called with a context that does not contain a OverlayToolWrapper.'),
]);
}
}
class OverlayToolWrapperState extends State<OverlayToolWrapper>
with SingleTickerProviderStateMixin {
bool show = false;
Offset offset = Offset(200, 200);
AnimationController _ctrl;
final double width = 200;
final double height = 30;
final double outWidth = 35;
final double boxHeight = 110;
final double radius = 60;
OverlayEntry entry;
double showWidth = 0;
bool out = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((callback) {
var px = MediaQuery.of(context).size.width - (outWidth);
var py = 120.0;
offset = Offset(px, py);
_ctrl = AnimationController(
duration: Duration(milliseconds: 400),
vsync: this,
lowerBound: 0,
upperBound: width - outWidth)
..addListener(_listenAnimate);
entry = OverlayEntry(
builder: (context) => Stack(
children: <Widget>[
Positioned(
left: offset.dx,
top: offset.dy,
child: _buildFloating(),
),
],
));
});
}
void _listenAnimate() {
var px = MediaQuery.of(context).size.width - (outWidth);
offset = Offset(px - (_ctrl.value), offset.dy);
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);
if (x < px - (width - outWidth)) {
x = px - (width - outWidth);
out = true;
}
if (x > px) {
out = false;
x = px;
}
if (y > MediaQuery.of(context).size.height - 50) {
y = MediaQuery.of(context).size.height - 50;
}
offset = Offset(x, y - boxHeight / 2);
entry.markNeedsBuild();
},
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))),
)),
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();
}
}),
],
),
),
),
],
),
);
Widget buildItem(IconData icon, Function onPress) {
return FeedbackWidget(
onPressed: () {
onPress();
close();
},
child: Circle(
radius: 12,
color: Theme.of(context).primaryColor,
child: Icon(
icon,
size: 15,
color: Colors.white,
),
),
);
}
void open() {
if (out) return;
// _ctrl.forward();
var px = MediaQuery.of(context).size.width - (outWidth);
offset = Offset(px - (width - outWidth), offset.dy);
entry.markNeedsBuild();
out = true;
}
void close() {
if (!out) return;
var px = MediaQuery.of(context).size.width - (outWidth);
offset = Offset(px, offset.dy);
entry.markNeedsBuild();
out = false;
}
// void toggle() {
// print('=====$out============');
// if (out) {
// _ctrl.reverse();
// } else {
// _ctrl.forward();
// }
// out = !out;
// // entry.markNeedsBuild();
// }
showFloating() {
if (!show) {
Overlay.of(context).insert(entry);
show = true;
}
}
hideFloating() {
if (show) {
entry.remove();
show = false;
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}