【问题标题】:Is there a way to have a floatingActionButton and a SpeedDial together?有没有办法同时拥有一个 floatingActionButton 和一个 SpeedDial?
【发布时间】:2021-03-18 02:37:19
【问题描述】:

我正在尝试使用 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).

【问题讨论】:

标签: flutter dart speed-dial


【解决方案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,
        ),
      ],
    ),
  );
}

这会给出这个结果:

【讨论】:

  • 我试过了,它不会像“原始”那样模糊背景。如果这对您来说不是问题,它可以正常工作。我现在正在检查一个名为 BackdropFilter 的小部件,看看我是否可以让它变得模糊。谢谢!
【解决方案2】:

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


看起来像这样:

【讨论】:

    【解决方案3】:

    我修改了the approach of 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(),
                  ],
                ),
              ],
            ),
    

    结果如下:

    【讨论】:

      猜你喜欢
      • 2018-10-26
      • 2011-02-12
      • 1970-01-01
      • 1970-01-01
      • 2022-08-24
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      相关资源
      最近更新 更多