办法一

  Offset _offset = Offset.zero;  Scaffold(        body: Stack(          children: [            _pageList[_currentIndex],            Positioned(              left: _offset.dx,              top: _offset.dy,              child: GestureDetector(                onPanUpdate: (d) =>                    setState(() => _offset += Offset(d.delta.dx, d.delta.dy)),                child: FloatingActionButton(                  onPressed: () {},                  backgroundColor: Colors.orange,                  child: Icon(Icons.add),                ),              ),            ),          ],        ),

办法二

 WidgetsBinding.instance        .addPostFrameCallback((_) => _insertOverlay(context));
void _insertOverlay(BuildContext context) {    return Overlay.of(context).insert(      OverlayEntry(builder: (context) {        final size = MediaQuery.of(context).size;        print(size.width);        return Positioned(          width: 56,          height: 56,          top: size.height - 72,          left: size.width - 72,          child: Material(            color: Colors.transparent,            child: GestureDetector(              onTap: () => print('ON TAP OVERLAY!'),              child: Container(                decoration: BoxDecoration(                    shape: BoxShape.circle, color: Colors.redAccent),              ),            ),          ),        );      }),    );  }

办法三

1.场景
当初须要做一个Test按钮,悬浮在所有页面之上,并且能够拖拽。

2.思路
1)悬浮按钮能够应用flutter提供的Overlay + OverlayEntry 组合实现

2)拖拽性能能够应用GestureDetector手势按钮或者Draggable实现(PS:我做了一版Draggable实现的,然而发现它会有本来的widget浮在原地,显然不是我要的成果)

3)点击的时候我是让它弹出一个底部弹框,这里你们能够自由发挥,本篇文章不做多余赘述

PubScaffold(      child: MaterialApp(        theme: CustomTheme.lightTheme,        darkTheme: CustomTheme.darkTheme,        themeMode: currentTheme.currentTheme,        home: Scaffold(          body: Stack(            children: [              _pageList[_currentIndex],              // Positioned(              //   left: _offset.dx,              //   top: _offset.dy,              //   child: GestureDetector(              //     onPanUpdate: (d) =>              //         setState(() => _offset += Offset(d.delta.dx, d.delta.dy)),              //     child: FloatingActionButton(              //       onPressed: () {},              //       backgroundColor: Colors.orange,              //       child: Icon(Icons.add),              //     ),              //   ),              // ),            ],          ),          bottomNavigationBar: CurvedNavigationBar(            // key: _bottomNavigationKey,            index: 0,            height: 60.0,            items: <Widget>[              Icon(Icons.home, size: 30),              Icon(Icons.list, size: 30),              Icon(Icons.compare_arrows, size: 30),              // Icon(Icons.call_split, size: 30),            ],            color: Colors.white,            buttonBackgroundColor: Colors.white,            backgroundColor: Colors.blueAccent,            animationCurve: Curves.easeInOut,            animationDuration: Duration(milliseconds: 600),            onTap: (index) {              setState(() {                _currentIndex = index;              });            },            // letIndexChange: (index) => true,          ),        ),      ),    );

这里的PubScaffold就是我封装的一个悬浮按钮组件,把它包裹在MaterialApp里面,就能够实现悬浮在所有的组件之上的一个按钮啦(当然也能够不是按钮,具体款式能够本人定义)。上面咱们来看一下PubScaffold中的代码吧~

import 'dart:math';import 'package:flutter/material.dart';class PubScaffold extends StatefulWidget {  final Widget child;  PubScaffold({this.child});  @override  _PubScaffoldState createState() => _PubScaffoldState();}class _PubScaffoldState extends State<PubScaffold> {  bool draggable = false;  //静止状态下的offset  Offset idleOffset = Offset(0, 0);  //本次挪动的offset  Offset moveOffset = Offset(0, 0);  //最初一次down事件的offset  Offset lastStartOffset = Offset(0, 0);  int count = 0;  final List<String> testWidgetList = [    '测试1',    '测试2',  ];  testAppFun(e) {    // TODO: 你的代码逻辑  }  // 显示一个底部弹窗,这里是一个测试列表  showTestList() {    showModalBottomSheet(      context: context,      enableDrag: false,      shape: RoundedRectangleBorder(        borderRadius: BorderRadius.only(          topLeft: Radius.circular(20.0),          topRight: Radius.circular(20.0),        ),      ),      builder: (BuildContext context) {        return ListView(          children: testWidgetList              .map(                (e) => Container(                  decoration: BoxDecoration(                    border: Border(                      bottom: BorderSide(color: Color(0xFFe3e3e3)),                    ),                  ),                  child: ListTile(                    onTap: () => testAppFun(e),                    title: Text(e),                  ),                ),              )              .toList(),        );      },    );  }  @override  Widget build(BuildContext context) {    return LayoutBuilder(      builder: (context, constraints) {        // 显示悬浮按钮        WidgetsBinding.instance            .addPostFrameCallback((_) => _insertOverlay(context));        return widget.child;      },    );  }  // 悬浮按钮,能够拖拽(可自定义款式)  void _insertOverlay(BuildContext context) {    return Overlay.of(context).insert(      OverlayEntry(builder: (context) {        final size = MediaQuery.of(context).size;        print(size.width);        return Positioned(          top: draggable ? moveOffset.dy : size.height - 102,          left: draggable ? moveOffset.dx : size.width - 72,          child: GestureDetector(            // 挪动开始            onPanStart: (DragStartDetails details) {              setState(() {                lastStartOffset = details.globalPosition;                draggable = 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 - 72)),                      moveOffset.dy + (size.height - 102));                }              });            },            // 挪动完结            onPanEnd: (DragEndDetails detail) {              setState(() {                idleOffset = moveOffset * 1;              });            },            child: TestContainer(              onPress: () => showTestList(),            ),          ),        );      }),    );  }}// 悬浮按钮的款式class TestContainer extends StatelessWidget {  final Function onPress;  TestContainer({this.onPress});  @override  Widget build(BuildContext context) {    return Material(      color: Colors.transparent,      child: GestureDetector(        onTap: onPress,        child: Container(          width: 56,          height: 56,          alignment: Alignment.center,          decoration: BoxDecoration(            shape: BoxShape.circle,            color: Colors.green[600],          ),          child: Text(            "Test",            style: TextStyle(              color: Colors.white,              fontWeight: FontWeight.bold,            ),          ),        ),      ),    );  }}

1.全局悬浮按钮

这里咱们用的是flutter自带的material库中的Overlay组件,具体应用办法如下:

void _insertOverlay(BuildContext context) {    return Overlay.of(context).insert(      OverlayEntry(builder: (context) {        final size = MediaQuery.of(context).size;        print(size.width);        return Positioned(          top: draggable ? moveOffset.dy : size.height - 102,          left: draggable ? moveOffset.dx : size.width - 72,          child: GestureDetector(            // 挪动开始            onPanStart: (DragStartDetails details) {              setState(() {                lastStartOffset = details.globalPosition;                draggable = 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 - 72)),                      moveOffset.dy + (size.height - 102));                }              });            },            // 挪动完结            onPanEnd: (DragEndDetails detail) {              setState(() {                idleOffset = moveOffset * 1;              });            },            child: TestContainer(              onPress: () => showTestList(),            ),          ),        );      }),    );  }