0%

Flutter-Dart全局悬浮球

当我们全局都需要用到某个设定且随时需要根据需求改变时,那么全局悬浮球是一个最好的选择,参考其他大佬的文章,封装了一个简易的悬浮球,记录一下0.0。

Dart全局悬浮球

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class PubScaffold extends StatefulWidget {
final Widget child;
PubScaffold({this.child});

@override
_PubScaffoldState createState() => _PubScaffoldState();
}

class _PubScaffoldState extends State<PubScaffold> {
List _bottomSheetList = ['x','y','z'];
bool dragAble = false;
// bottomSheet是否已经显示
bool isShow = false;

// 静止状态下的offset
Offset idleOffset = Offset(0, 0);
// 本次移动的offset
Offset moveOffset = Offset(0, 0);
// 最后一次down事件的offset
Offset lastStartOffset = Offset(0, 0);

int count = 0;
static OverlayEntry entry;

/// 列表点击事件
selectItemCallBack(e) {
print('选中${e}');
if (isShow) {
Navigator.pop(context);
}
}

/// 显示一个底部弹窗,这里是一个测试列表。
showSelectList() async {
if (isShow) {
Navigator.pop(context);
return;
}
var flag = await showModalBottomSheet(
context: context,
enableDrag: false,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
builder: (BuildContext context) {
isShow = true;
return SizedBox(
// 返回一个有高度的SizedBox,对话框高度就是此高度。
height: 285,
child: ListView(
children: _bottomSheetList.map((e) =>
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFe3e3e3)),
),
),
child: ListTile(
onTap: () => selectItemCallBack(e),
title: Text(e),
),
)).toList()
),
);
},
);
if (flag == null) {
isShow = false;
}
}

@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
// 显示悬浮按钮
WidgetsBinding.instance.addPostFrameCallback((_) => _insertOverlay(context));
return widget.child;
},
);
}

// 悬浮按钮,可以拖拽(可自定义样式)
void _insertOverlay(BuildContext context) {
entry = OverlayEntry(builder: (context) {
final size = MediaQuery.of(context).size;
print(size.width);
return Positioned(
top: dragAble ? moveOffset.dy : size.height - 310,
left: dragAble ? moveOffset.dx : size.width - 70,
child: GestureDetector(
// 移动开始
onPanStart: (DragStartDetails details) {
setState(() {
lastStartOffset = details.globalPosition;
dragAble = true;
});
if (count <= 1) {
count++;
}
},
// 移动中
onPanUpdate: (DragUpdateDetails details) {
setState(() {
moveOffset = details.globalPosition - lastStartOffset + idleOffset;
if (count > 1) {
moveOffset = Offset(max(0, moveOffset.dx), moveOffset.dy);
} else {
moveOffset = Offset(max(0, moveOffset.dx + (size.width - 70)), moveOffset.dy + (size.height - 310));
}
});
},
// 移动结束
onPanEnd: (DragEndDetails detail) {
setState(() {
idleOffset = moveOffset * 1;
});
},
child: BallContainer(
onPressed: () => showSelectList(),
),
),
);
});
}
}

/// 悬浮按钮的样式
class BallContainer extends StatelessWidget {
final Function onPressed;
BallContainer({this.onPressed});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: GestureDetector(
onTap: onPressed,
child: Container(
width: 45,
height: 45,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0x666889E6),
),
child: Text(
'球体内容',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}

使用

在主程序 main.dart 套上我们的 PubScaffold 即可。

1
2
3
4
5
6
7
8
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
builder: EasyLoading.init(),
home: PubScaffold(child: ...略)
);
}
bulb