当我们全局都需要用到某个设定且随时需要根据需求改变时,那么全局悬浮球是一个最好的选择(可拖动),参考其他大佬的文章,优化封装了一个简易的悬浮球,记录一下0.0。
Dart全局悬浮球
代码语言:javascript复制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 {
KeyboardBack.keyboardBack();
if (isShow) {
Navigator.pop(context);
return;
}
var flag = await showModalBottomSheet(
isScrollControlled: true,
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 SingleChildScrollView(
child: Container(
// 返回一个有高度的组件,对话框高度就是此高度。
padding: MediaQuery.of(context).viewInsets,
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;
double maxWidth = size.width - 50;
double maxHeight = size.height - 50;
double defaultX = size.width - 70;
double defaultY = size.height - 310;
return Positioned(
top: draggable
? (moveOffset.dy < 0 // 沉浸式如果超出屏幕,默认为 0。
? 0
: moveOffset.dy > maxHeight // 其他情况超出屏幕默认为 maxHeight。
? maxHeight
: moveOffset.dy)
: defaultY,
left: draggable ? (moveOffset.dx > maxWidth ? maxWidth : moveOffset.dx) : defaultX,
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: 50,
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0x666889E6),
),
child: Text(
'球体内容',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}
/// 关闭键盘,避免键盘与弹出列表冲突。
class KeyboardBack {
static BuildContext context = navigatorKey.currentState.overlay.context;
static FocusScopeNode currentFocus = FocusScope.of(context);
static void keyboardBack() {
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus.unfocus();
}
}
}
使用
代码语言:javascript复制在主程序 main.dart 套上我们的 PubScaffold 即可。
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
builder: EasyLoading.init(),
home: PubScaffold(child: ...略)
);
}