Flutter fabの使い方を説明するために上に吹き出しをつける

f:id:ta_watanabe:20211022163002p:plain

言葉で説明しなくてもいいのがマテリアルデザインですが、 こういう依頼が来ることもたまにありますよね。

Bubbleもパッケージがあるのですが、矢印の部分(三角のところ)が自由に移動できなかったので、 自作しました。

import 'package:flutter/material.dart';

class Bubble extends StatelessWidget {
  Bubble({Key? key, required this.text, required this.textStyle})
      : super(key: key);

  final String text;
  final TextStyle textStyle;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      padding: const EdgeInsets.only(left: 32, top: 16, right: 32, bottom: 8),
      // ここにconstつけるとhot reloadで変わらない
      decoration: ShapeDecoration(
        color: Colors.blue,
        shadows: [
          BoxShadow(
            color: Color(0x80000000),
            offset: Offset(0, 2),
            blurRadius: 2,
          )
        ],
        shape: BubbleBorder(),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            text,
            style: textStyle,
          ),
        ],
      ),
    );
  }
}

class BubbleBorder extends ShapeBorder {
  final bool usePadding;

  const BubbleBorder({this.usePadding = true});

  @override
  EdgeInsetsGeometry get dimensions => const EdgeInsets.all(0);

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) => Path();

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    return Path()
      // xは少なくなると左、多くなると右
      // yは少なくなると上に移動する
      ..moveTo(rect.bottomCenter.dx + 28, rect.bottomCenter.dy)
      ..relativeLineTo(45, 16)
      ..relativeLineTo(6, -16)
      ..addRRect(RRect.fromRectAndRadius(rect, const Radius.circular(8)))
      ..close();
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}

  @override
  ShapeBorder scale(double t) => this;
}

上のコードを呼び出すmain

import 'package:flutter/material.dart';

import 'bubble.dart';

void main() {
  runApp(MaterialApp(
    home: MySca(),
  ));
}

class MySca extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('fabの上に吹き出し'),
        ),
        floatingActionButton: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          mainAxisSize: MainAxisSize.min,
          children: [
            Padding(
              padding: const EdgeInsets.only(right: 0),
              child: Bubble(
                  text: 'こちらを押すと新規登録です',
                  textStyle: const TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 18)),
            ),
            Padding(
                padding: const EdgeInsets.only(top: 16),
                child: FloatingActionButton(
                  child: Icon(Icons.add),
                  onPressed: () {},
                )),
          ],
        ),
        body: SizedBox.shrink());
  }
}

https://github.com/na8esin/flutter2_practice/tree/main/lib/src/bubble