1

我正在尝试使用 SpeedDial,我想知道是否可以在同一个屏幕中使用 SpeedDial 和 floatingActionButton。

下一个代码对我来说有两个 FloatingActionButton,但是当添加快速拨号时,屏幕的限制会变得很糟糕(它说底部被无限像素溢出)。

  Widget build(BuildContext context) {
    return Scaffold(
        appBar: searchBar.build(context),
        body: Container(),
        floatingActionButton: Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            buildSpeedDial(),
            FloatingActionButton(
              onPressed: (){},
              child: Icon(Icons.add),
              backgroundColor: Colors.green,
            ),
            FloatingActionButton(
              onPressed: (){},
              child: Icon(Icons.add),
              backgroundColor: Colors.green,
            ),
          ],
        )
    );
  }

我使用的 buildSpeedDial() 与flutter_speed_dial 1.2.5 page的示例相同。

控制台显示:

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).
4

3 回答 3

1

StackSpeedDial在构建自身时正在使用该小部件。所以,我也有一个使用 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,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}


它看起来像这样:

在此处输入图像描述

在此处输入图像描述

于 2020-12-06T17:54:05.137 回答
1

我修改了 Akif 的方法,使两个按钮一个在另一个之上。结果如下:


floatingActionButton: Stack(
          alignment: Alignment.bottomRight,
          children: [
            Positioned(
              bottom: 70,
              child: Container(
                child: FloatingActionButton(
                  onPressed: (){
                    Navigator.push(context, MaterialPageRoute(builder:(context)=> FormNewActivity()));
                  },
                  child: Icon(Icons.add,color:Colors.white,size: 30),
                  backgroundColor: Colors.green,
                )
              ),
            ),
            Stack(
              alignment: Alignment.bottomRight,
              children: [
                buildSpeedDial(),
              ],
            ),
          ],
        ),

结果如下所示:

关闭

打开

于 2020-12-06T20:01:08.773 回答
1

一种解决方案是限制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,
        ),
      ],
    ),
  );
}

这将给出这个结果:

示例应用

于 2020-12-06T17:23:12.040 回答