Flutter Webで右下のお問い合わせチャットモーダル

例によってFlutter Webって書いたけど別にスマホでも動く

# こういうやつ

<img src="https://i.gyazo.com/6fe4e72a21aa4438e81e7ad54b67ce12.gif" width=300 />

# ソース

概要はこんな感じ
・`Scaffold`の`floatingActionButton`にでかい`Container`置いてそこにチャットボードの`Card`と`FloatingActionButton`を`Stack`で配置してる
・チャットボードの位置と透明度をアニメーションで変更して出し入れ

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isClose = true; //チャットボックスが開いているか
  var _opacity = 0.0; //チャットボックスの透明度
  var _position = 40.0; //チャットボックスの位置
  var _icon; //FABのアイコン

  @override
  void initState() {
    _isClose = true;
    _opacity = 0.0;
    _position = 40.0;
    _icon = Icon(Icons.message);
    super.initState();
  }

  //チャットボードとFABを生成するメソッド
  List<Widget> createFloatingActionButtons() {
    var chatBoard = Card(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Container(
            width: 300,
            height: 40,
            color: Colors.blue,
            child: Center(
              child: Text(
                "質問をどうぞ！",
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
          Container(
            color: Colors.white,
            width: 300,
            height: 450,
          ),
        ],
      ),
    );

    var questionButton = FloatingActionButton(
      elevation: 0,
      hoverElevation: 0,
      focusElevation: 0,
      highlightElevation: 0,
      child: _icon,
      backgroundColor: Colors.blueAccent,
      onPressed: () {
        setState(() {
          _isClose = !_isClose;
          if (_isClose) {
            _opacity = 0.0;
            _position = 40;
            _icon = Icon(Icons.message);
          } else {
            _opacity = 1.0;
            _position = 60;
            _icon = Icon(Icons.close);
          }
        });
      },
    );

    return [
      questionButton,
      AnimatedPositioned(
        duration: Duration(milliseconds: 200),
        right: 0,
        bottom: _position,
        child: AnimatedOpacity(
          duration: Duration(milliseconds: 200),
          opacity: _opacity,
          child: chatBoard,
        ),
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> floatingActionButtons = createFloatingActionButtons();

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: Container(
          color: Colors.white,
          child: Center(
            child: Text("よくある問い合わせのやつ"),
          ),
        ),
      ),
      floatingActionButton: Container(
        width: 1000,
        height: 1000,
        child: Stack(
          alignment: Alignment.bottomRight,
          children: floatingActionButtons,
        ),
      ),
    );
  }
}

```