Flutter 有没有办法将浮动操作按钮和快速拨号盘结合在一起?

Flutter 有没有办法将浮动操作按钮和快速拨号盘结合在一起?,flutter,dart,speed-dial,Flutter,Dart,Speed Dial,我正在试验SpeedDial,我想知道是否有可能在同一屏幕上有一个SpeedDial和一个FloatingAction按钮 下一个代码为我提供了两个FloatingActionButton,但是当添加speeddial时,屏幕的限制变得非常糟糕(它说底部溢出了无限像素) 我使用的buildSpeedDial()与的示例中相同 控制台显示以下内容: Error: Cannot hit test a render box that has never been laid out. The hitTe

我正在试验SpeedDial,我想知道是否有可能在同一屏幕上有一个SpeedDial和一个FloatingAction按钮

下一个代码为我提供了两个FloatingActionButton,但是当添加speeddial时,屏幕的限制变得非常糟糕(它说底部溢出了无限像素)

我使用的buildSpeedDial()与的示例中相同

控制台显示以下内容:

Error: Cannot hit test a render box that has never been laid out.
The hitTest() method was called on this RenderBox: RenderFlex#aa804 NEEDS-LAYOUT NEEDS-PAINT:
  needs compositing
  creator: Column ← Container ← Positioned ← Stack ← SpeedDial ← Column ← Transform ← RotationTransition ← Transform ← ScaleTransition ← Stack ← _FloatingActionButtonTransition ← ⋯
  parentData: right=0.0; bottom=0.0; offset=Offset(0.0, 0.0)
  constraints: MISSING
  size: MISSING
  direction: vertical
  mainAxisAlignment: end
  mainAxisSize: max
  crossAxisAlignment: end
  textDirection: ltr
  verticalDirection: down
Unfortunately, this object's geometry is not known at this time, probably because it has never been laid out. This means it cannot be accurately hit-tested.
If you are trying to perform a hit test during the layout phase itself, make sure you only hit test nodes that have completed layout (e.g. the node's children, after their layout() method has been called).

一种解决方案是将
speedDial
限制如下:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Flutter Speed Dial')),
    body: buildBody(),
    floatingActionButton: Column(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        SizedBox(
          height: 60,
          width: 60,
          child: buildSpeedDial(),
        ),
        SizedBox(height: 10),
        FloatingActionButton(
          onPressed: () {},
          child: Icon(Icons.add),
          backgroundColor: Colors.green,
        ),
        SizedBox(height: 10),
        FloatingActionButton(
          onPressed: () {},
          child: Icon(Icons.add),
          backgroundColor: Colors.green,
        ),
      ],
    ),
  );
}
这将产生以下结果:


一种解决方案是将
快速拨号限制如下:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Flutter Speed Dial')),
    body: buildBody(),
    floatingActionButton: Column(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        SizedBox(
          height: 60,
          width: 60,
          child: buildSpeedDial(),
        ),
        SizedBox(height: 10),
        FloatingActionButton(
          onPressed: () {},
          child: Icon(Icons.add),
          backgroundColor: Colors.green,
        ),
        SizedBox(height: 10),
        FloatingActionButton(
          onPressed: () {},
          child: Icon(Icons.add),
          backgroundColor: Colors.green,
        ),
      ],
    ),
  );
}
这将产生以下结果:


SpeedDial在构建自身时正在使用
堆栈
小部件。因此,我也有一个使用Stack的肮脏解决方案:


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

import 'package:flutter_speed_dial/flutter_speed_dial.dart';

void main() {
  runApp(MaterialApp(home: MyApp(), title: 'Flutter Speed Dial Examples'));
}

class MyApp extends StatefulWidget {
  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> with TickerProviderStateMixin {
  ScrollController scrollController;
  bool dialVisible = true;

  @override
  void initState() {
    super.initState();

    scrollController = ScrollController()
      ..addListener(() {
        setDialVisible(scrollController.position.userScrollDirection ==
            ScrollDirection.forward);
      });
  }

  void setDialVisible(bool value) {
    setState(() {
      dialVisible = value;
    });
  }

  Widget buildBody() {
    return ListView.builder(
      controller: scrollController,
      itemCount: 30,
      itemBuilder: (ctx, i) => ListTile(title: Text('Item $i')),
    );
  }

  SpeedDial buildSpeedDial() {
    return SpeedDial(
      animatedIcon: AnimatedIcons.menu_close,
      animatedIconTheme: IconThemeData(size: 22.0),
      // child: Icon(Icons.add),
      onOpen: () => print('OPENING DIAL'),
      onClose: () => print('DIAL CLOSED'),
      visible: dialVisible,
      curve: Curves.bounceIn,
      children: [
        SpeedDialChild(
          child: Icon(Icons.accessibility, color: Colors.white),
          backgroundColor: Colors.deepOrange,
          onTap: () => print('FIRST CHILD'),
          label: 'First Child',
          labelStyle: TextStyle(fontWeight: FontWeight.w500),
          labelBackgroundColor: Colors.deepOrangeAccent,
        ),
        SpeedDialChild(
          child: Icon(Icons.brush, color: Colors.white),
          backgroundColor: Colors.green,
          onTap: () => print('SECOND CHILD'),
          label: 'Second Child',
          labelStyle: TextStyle(fontWeight: FontWeight.w500),
          labelBackgroundColor: Colors.green,
        ),
        SpeedDialChild(
          child: Icon(Icons.keyboard_voice, color: Colors.white),
          backgroundColor: Colors.blue,
          onTap: () => print('THIRD CHILD'),
          labelWidget: Container(
            color: Colors.blue,
            margin: EdgeInsets.only(right: 10),
            padding: EdgeInsets.all(6),
            child: Text('Custom Label Widget'),
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter Speed Dial')),
      body: buildBody(),
      floatingActionButton: Stack(
        alignment: Alignment.bottomRight,
        fit: StackFit.expand,
        overflow: Overflow.visible,
        children: [
          Stack(
            alignment: Alignment.bottomRight,
            fit: StackFit.expand,
            overflow: Overflow.visible,
            children: [
              buildSpeedDial(),
            ],
          ),
          // Here is a FAB
          Stack(
            alignment: Alignment.bottomCenter,
            children: [
              FloatingActionButton(
                onPressed: () {
                  print("object");
                },
                child: Icon(Icons.add),
                backgroundColor: Colors.green,
              ),
            ],
          ),
          // Here, one more FAB!
          Stack(
            alignment: Alignment.bottomLeft,
            children: [
              Padding(
                padding: const EdgeInsets.fromLTRB(40, 0, 0, 0),
                child: FloatingActionButton(
                  onPressed: () {
                    print("object");
                  },
                  child: Icon(Icons.remove),
                  backgroundColor: Colors.red,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}



进口“包装:颤振/材料.省道”;
导入“package:flatter/rendering.dart”;
进口“包装:颤振速度刻度盘/颤振速度刻度盘.省道”;
void main(){
runApp(MaterialApp(主页:MyApp(),标题:“颤振速度拨号示例”);
}
类MyApp扩展了StatefulWidget{
@凌驾
MyAppState createState()=>MyAppState();
}
类MyAppState使用TickerProviderStateMixin扩展状态{
滚动控制器滚动控制器;
bool dialVisible=true;
@凌驾
void initState(){
super.initState();
scrollController=scrollController()
…addListener(){
setDialVisible(scrollController.position.userScrollDirection==
滚动方向(向前);
});
}
无效setDialVisible(布尔值){
设置状态(){
dialVisible=值;
});
}
Widget buildBody(){
返回ListView.builder(
控制器:滚动控制器,
物品计数:30,
itemBuilder:(ctx,i)=>ListTile(标题:Text('Item$i')),
);
}
SpeedDial buildSpeedDial(){
返回快速拨号(
animatedIcon:AnimatedIcons.menu\u关闭,
animatedIconTheme:IconThemeData(大小:22.0),
//子:图标(Icons.add),
onOpen:()=>打印('打开拨号'),
onClose:()=>打印('拨号关闭'),
可见:拨号可见,
曲线:Curves.bounceIn,
儿童:[
快速拨号儿童(
子项:图标(图标.可访问性,颜色:颜色.白色),
背景颜色:Colors.deepOrange,
onTap:()=>print('FIRST CHILD'),
标签:“第一个孩子”,
labelStyle:TextStyle(fontWeight:fontWeight.w500),
labelBackgroundColor:Colors.deepOrangeAccent,
),
快速拨号儿童(
子项:图标(Icons.brush,颜色:Colors.white),
背景颜色:Colors.green,
onTap:()=>print('SECOND CHILD'),
标签:“第二个孩子”,
labelStyle:TextStyle(fontWeight:fontWeight.w500),
labelBackgroundColor:Colors.green,
),
快速拨号儿童(
子:图标(图标.键盘\声音,颜色:颜色.白色),
背景颜色:Colors.blue,
onTap:()=>print('THIRD CHILD'),
labelWidget:容器(
颜色:颜色,蓝色,
页边距:仅限边集(右:10),
填充:边缘设置。全部(6),
子项:文本(“自定义标签小部件”),
),
),
],
);
}
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(标题:文本(“颤振速度拨号”),
body:buildBody(),
浮动操作按钮:堆栈(
对齐:对齐。右下角,
fit:StackFit.expand,
溢出:溢出。可见,
儿童:[
堆叠(
对齐:对齐。右下角,
fit:StackFit.expand,
溢出:溢出。可见,
儿童:[
buildSpeedDial(),
],
),
//这是一个晶圆厂
堆叠(
对齐:对齐.bottomCenter,
儿童:[
浮动操作按钮(
已按下:(){
打印(“对象”);
},
子:图标(Icons.add),
背景颜色:Colors.green,
),
],
),
//来,再来一个晶圆厂!
堆叠(
对齐:对齐。左下角,
儿童:[
填充物(
填充:从LTRB(40,0,0,0)开始的常量边集,
子:浮动操作按钮(
已按下:(){
打印(“对象”);
},
子:图标(图标。删除),
背景颜色:Colors.red,
),
),
],
),
],
),
);
}
}
它将如下所示:


SpeedDial在构建自身时正在使用
堆栈
小部件。因此,我也有一个使用Stack的肮脏解决方案:


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

import 'package:flutter_speed_dial/flutter_speed_dial.dart';

void main() {
  runApp(MaterialApp(home: MyApp(), title: 'Flutter Speed Dial Examples'));
}

class MyApp extends StatefulWidget {
  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> with TickerProviderStateMixin {
  ScrollController scrollController;
  bool dialVisible = true;

  @override
  void initState() {
    super.initState();

    scrollController = ScrollController()
      ..addListener(() {
        setDialVisible(scrollController.position.userScrollDirection ==
            ScrollDirection.forward);
      });
  }

  void setDialVisible(bool value) {
    setState(() {
      dialVisible = value;
    });
  }

  Widget buildBody() {
    return ListView.builder(
      controller: scrollController,
      itemCount: 30,
      itemBuilder: (ctx, i) => ListTile(title: Text('Item $i')),
    );
  }

  SpeedDial buildSpeedDial() {
    return SpeedDial(
      animatedIcon: AnimatedIcons.menu_close,
      animatedIconTheme: IconThemeData(size: 22.0),
      // child: Icon(Icons.add),
      onOpen: () => print('OPENING DIAL'),
      onClose: () => print('DIAL CLOSED'),
      visible: dialVisible,
      curve: Curves.bounceIn,
      children: [
        SpeedDialChild(
          child: Icon(Icons.accessibility, color: Colors.white),
          backgroundColor: Colors.deepOrange,
          onTap: () => print('FIRST CHILD'),
          label: 'First Child',
          labelStyle: TextStyle(fontWeight: FontWeight.w500),
          labelBackgroundColor: Colors.deepOrangeAccent,
        ),
        SpeedDialChild(
          child: Icon(Icons.brush, color: Colors.white),
          backgroundColor: Colors.green,
          onTap: () => print('SECOND CHILD'),
          label: 'Second Child',
          labelStyle: TextStyle(fontWeight: FontWeight.w500),
          labelBackgroundColor: Colors.green,
        ),
        SpeedDialChild(
          child: Icon(Icons.keyboard_voice, color: Colors.white),
          backgroundColor: Colors.blue,
          onTap: () => print('THIRD CHILD'),
          labelWidget: Container(
            color: Colors.blue,
            margin: EdgeInsets.only(right: 10),
            padding: EdgeInsets.all(6),
            child: Text('Custom Label Widget'),
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter Speed Dial')),
      body: buildBody(),
      floatingActionButton: Stack(
        alignment: Alignment.bottomRight,
        fit: StackFit.expand,
        overflow: Overflow.visible,
        children: [
          Stack(
            alignment: Alignment.bottomRight,
            fit: StackFit.expand,
            overflow: Overflow.visible,
            children: [
              buildSpeedDial(),
            ],
          ),
          // Here is a FAB
          Stack(
            alignment: Alignment.bottomCenter,
            children: [
              FloatingActionButton(
                onPressed: () {
                  print("object");
                },
                child: Icon(Icons.add),
                backgroundColor: Colors.green,
              ),
            ],
          ),
          // Here, one more FAB!
          Stack(
            alignment: Alignment.bottomLeft,
            children: [
              Padding(
                padding: const EdgeInsets.fromLTRB(40, 0, 0, 0),
                child: FloatingActionButton(
                  onPressed: () {
                    print("object");
                  },
                  child: Icon(Icons.remove),
                  backgroundColor: Colors.red,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}



进口“包装:颤振/材料.省道”;
导入“package:flatter/rendering.dart”;
进口“包装:颤振速度刻度盘/颤振速度刻度盘.省道”;
void main(){
runApp(MaterialApp(主页:MyApp(),标题:“颤振速度拨号示例”);
}
类MyApp扩展了StatefulWidget{
@凌驾
MyAppState createState()=>MyAppState();
}
类MyAppState使用TickerProviderStateMixin扩展状态{
滚动控制器滚动控制器;
bool dialVisible=true;
@凌驾
void initState(){
super.initState();
scrollController=scrollController()
…addListener(){
setDialVisible(scrollController.position.userScrollDirection==
滚动方向(向前);
});
}
无效setDialVisible(布尔值){
设置状态(){
dialVisible=值;
});
}
Widget buildBody(){
返回ListView.builder(
控制器:滚动控制器,
物品计数:30,
itemBuilder:(ctx,i)=>ListTile(标题:Text('Item$i')),
);
}
SpeedDial buildSpeedDial(){
返回快速拨号(
animateDictions:animateDictions.menu\u clos