Home Flutter Cara membuat FAB circle menu pada flutter

Cara membuat FAB circle menu pada flutter

0
Cara membuat  FAB circle menu pada flutter

Assalamualaikum sobat baraja, kali ini saya akan membagikan tips nih cara membuat tampilan UI lebih cantik yaitu pada sebuah tombol Floating Action Bar atau (FAB).

Okee langsung aja ya, di main.dart nya silahkan diisi dengan kode berikut

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: FabCircularMenu(
          fabColor: Colors.blueGrey,
          child: Container(
            color: Colors.black87,
            child: Center(
                child: Padding(
              padding: const EdgeInsets.only(bottom: 256.0),
              child: Text('FAB Circle Menu',
                  textAlign: TextAlign.center,
                  style: TextStyle(color: Colors.white, fontSize: 36.0)),
            )),
          ),
          ringColor: Colors.white30,
          options: <Widget>[
            IconButton(
                icon: Icon(Icons.alarm),
                onPressed: () {},
                tooltip: 'Alarm',
                iconSize: 48.0,
                color: Colors.white),
            IconButton(
                icon: Icon(Icons.home),
                tooltip: 'Home',
                onPressed: () {},
                iconSize: 48.0,
                color: Colors.white),
            IconButton(
                icon: Icon(Icons.wallet_giftcard),
                tooltip: 'Gift',
                onPressed: () {},
                iconSize: 48.0,
                color: Colors.white),
            IconButton(
                icon: Icon(Icons.person),
                tooltip: 'Home',
                onPressed: () {},
                iconSize: 48.0,
                color: Colors.white),
            IconButton(
                icon: Icon(Icons.email),
                tooltip: 'Email',
                onPressed: () {},
                iconSize: 48.0,
                color: Colors.white),
          ],
        ),
      ),
    );
  }
}

kemudian buat file baru di folder lib nya dengan nama fab_circular_menu.dart

library fab_circular_menu;

import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'package:vector_math/vector_math.dart' as vector;

class FabCircularMenu extends StatefulWidget {
  final Widget child;
  final List<Widget> options;
  final Color ringColor;
  final double ringDiameter;
  final double ringWidth;
  final EdgeInsets fabMargin;
  final Color fabColor;
  final Icon fabOpenIcon;
  final Icon fabCloseIcon;
  final Duration animationDuration;

  FabCircularMenu(
      {@required this.child,
      @required this.options,
      this.ringColor = Colors.white,
      this.ringDiameter,
      this.ringWidth,
      this.fabMargin = const EdgeInsets.all(24.0),
      this.fabColor,
      this.fabOpenIcon = const Icon(Icons.menu),
      this.fabCloseIcon = const Icon(Icons.close),
      this.animationDuration = const Duration(milliseconds: 800)});

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

class _FabCircularMenuState extends State<FabCircularMenu>
    with SingleTickerProviderStateMixin {
  double ringDiameter;
  double ringWidth;
  Color fabColor;

  bool animating = false;
  bool open = false;
  AnimationController controller;
  Animation<double> scaleAnimation;
  Animation scaleCurve;
  Animation<double> rotateAnimation;
  Animation rotateCurve;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    ringDiameter =
        widget.ringDiameter ?? MediaQuery.of(context).size.width * 1.2;
    ringWidth = widget.ringWidth ?? ringDiameter / 7;
    fabColor = widget.fabColor ?? Theme.of(context).primaryColor;

    controller =
        AnimationController(duration: widget.animationDuration, vsync: this);

    scaleCurve = CurvedAnimation(
        parent: controller,
        curve: Interval(0.0, 0.4, curve: Curves.easeInOutCirc));
    scaleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(scaleCurve)
      ..addListener(() {
        setState(() {});
      });

    rotateCurve = CurvedAnimation(
        parent: controller,
        curve: Interval(0.4, 1.0, curve: Curves.easeInOutCirc));
    rotateAnimation = Tween<double>(begin: 1.0, end: 90.0).animate(rotateCurve)
      ..addListener(() {
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    final double bottom = -(scaleAnimation.value * ringDiameter / 2 -
        20.0 -
        (widget.fabMargin.bottom / 4));
    final double right = -(scaleAnimation.value * ringDiameter / 2 -
        20.0 -
        (widget.fabMargin.right / 4));

    return Stack(
      alignment: Alignment.bottomRight,
      children: <Widget>[
        widget.child,
        Positioned(
          bottom: bottom,
          right: right,
          child: Container(
            width: scaleAnimation.value * ringDiameter,
            height: scaleAnimation.value * ringDiameter,
            child: CustomPaint(
              foregroundPainter: _RingPainter(
                  ringColor: widget.ringColor,
                  ringWidth: scaleAnimation.value * ringWidth),
            ),
          ),
        ),
        Positioned(
          bottom: bottom,
          right: right,
          child: Container(
            width: scaleAnimation.value * ringDiameter,
            height: scaleAnimation.value * ringDiameter,
            child: Transform.rotate(
              angle: -(math.pi / rotateAnimation.value),
              child: Stack(
                  alignment: Alignment.center,
                  children: _applyTranslations(widget.options)),
            ),
          ),
        ),
        Padding(
          padding: widget.fabMargin,
          child: FloatingActionButton(
              child: open ? widget.fabCloseIcon : widget.fabOpenIcon,
              backgroundColor: fabColor,
              onPressed: () {
                if (!animating && !open) {
                  animating = true;
                  open = true;
                  controller.forward().then((_) {
                    animating = false;
                  });
                } else if (!animating) {
                  animating = true;
                  open = false;
                  controller.reverse().then((_) {
                    animating = false;
                  });
                }
              }),
        )
      ],
    );
  }

  List<Widget> _applyTranslations(List<Widget> widgets) {
    return widgets
        .asMap()
        .map((index, widget) {
          final double angle = 80.0 / (widgets.length * 2 - 2) * (index * 2);
          return MapEntry(index, _applyTranslation(angle, widget));
        })
        .values
        .toList();
  }

  Widget _applyTranslation(double angle, Widget widget) {
    final double rad = vector.radians(angle);
    return Transform(
      transform: Matrix4.identity()
        ..translate(-(ringDiameter / 2) * math.cos(rad),
            -(ringDiameter / 2) * math.sin(rad)),
      child: widget,
    );
  }
}

class _RingPainter extends CustomPainter {
  final Color ringColor;
  final double ringWidth;

  _RingPainter({@required this.ringColor, @required this.ringWidth});

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = ringColor
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..strokeWidth = ringWidth;

    Offset center = Offset(size.width / 2, size.height / 2);

    canvas.drawCircle(center, size.width / 2, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

Maka outputnya kurang lebih seperti ini

Silahkan teman-teman modifikasi sendiri ya sesuai kebutuhan heheheh,, terima kasih 😁