将 FAB 转换为模态底部 sheet(或类似)

Transform FAB to modal bottom sheet (or similar)

我正在尝试使 FAB 展开成为底部 sheet,类似于 material.io

中的“屏幕内”示例

所以当我点击 this FAB 动画成为 this modal bottom sheet

下面是这个例子的代码:

import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Example'),
      ),
      body: Center(child: Text('Hello, World!')),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () async {
          showModalBottomSheet(
            context: context,
            backgroundColor: Colors.transparent,
            barrierColor: Colors.transparent,
            builder: (context) {
              return AddEntryBottomSheet();
            },
          );
        },
      ),
    );
  }
}

class AddEntryBottomSheet extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(8.0),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Padding(
            padding: const EdgeInsets.all(32.0),
            child: Text('Hello, world!'),
          ),
          Padding(
            padding: const EdgeInsets.all(32.0),
            child: Text('More content'),
          ),
        ],
      ),
    );
  }
}

我设法通过下面的示例获得了我想要的结果。只需将 ExpandingFab 用作脚手架中的 FAB

import 'package:flutter/material.dart';

class ExpandingFab extends StatefulWidget {
  @override
  _ExpandingFabState createState() => _ExpandingFabState();
}

class _ExpandingFabState extends State<ExpandingFab> {
  bool _cardIsOpen = false;
  double get cardWidth => MediaQuery.of(context).size.width - 32;
  double cardHeight = 200;

  Widget _renderFab() {
    return InkWell(
      onTap: () {
        setState(() {
          _cardIsOpen = true;
        });
      },
      child: Icon(Icons.add, color: Colors.white),
    );
  }

  Widget _renderUpsertEntryCard() {
    return Container(
      width: cardWidth,
      height: cardHeight,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Text('Hello, World!'),
          ElevatedButton(
            onPressed: () {
              setState(() {
                _cardIsOpen = false;
              });   
            }, 
            child: Text('Close'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    double w = _cardIsOpen ? cardWidth  : 56;
    double h = _cardIsOpen ? cardHeight : 56;

    return AnimatedContainer(
      curve: Curves.ease,
      constraints: BoxConstraints(
        minWidth: w,   maxWidth: w,
        minHeight: h,  maxHeight: h,
      ),
      duration: Duration(milliseconds: 300),
      decoration: BoxDecoration(
        color: _cardIsOpen ? Colors.blueGrey[100] : Colors.blue,
        boxShadow: kElevationToShadow[1],
        borderRadius: _cardIsOpen
            ? BorderRadius.all(Radius.circular(0.0))
            : BorderRadius.all(Radius.circular(50)),
      ),
      child: AnimatedSwitcher(
        duration: Duration(milliseconds: 200),
        transitionBuilder: (child, animation) => ScaleTransition(
          scale: animation,
          child: child,
        ),
        child: !_cardIsOpen ? _renderFab() : _renderUpsertEntryCard(),
      ),
    );
  }
}