Create circle ui navigator in flutter with library circle_ui_navigator

What does it look like?

Open Close
The example widget open The example widget close

Installation

  • Add circular_ui_navigator: 1.0.0 to your pubspec.yaml.
  • Run flutter pub get

This package requires Dart SDK version 2.19.0 or above to function properly.

Please note that only SVG assets are supported for icons, as they are a highly compatible cross-platform solution. While this should be sufficient for most projects, please be aware that other icon types are not currently supported.

Usage

To use the widget in your project, add the following import statement to the top of the file where you intend to use it:

import 'package:circle_ui_navigator/circle_ui_navigator.dart';

Then, use CircleNavigatorConfig in place of a widget. You can customize all of the parameters shown below, and a few more.

import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:circle_ui_navigator/circle_ui_navigator.dart';

/// An example of how to show the widget in the middle of the screen.
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Circle UI Navigator example',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Circle UI Navigator example'),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _isOpeningAnimation = true;
  bool _isClosingAnimation = false;

  @override
  Widget build(BuildContext context) {
    double pageWidth = context.screenWidth;
    double pageHeight = context.availableScreenHeight(
      statusBarHeight: MediaQueryData.fromWindow(window).padding.top,
      appBarHeight: 0,
      bottomNavigationBarHeight: kBottomNavigationBarHeight,
    );
    return CircleNavigatorConfig(
      config: Config(
        center: Point(pageWidth / 2, pageHeight / 2),
        animatedRippleColor: const Color(0xFF66A0FE).withOpacity(0.7),
        filledCircleColor: const Color(0xFFB4D8FF).withOpacity(0.7),
        isOpeningAnimation: _isOpeningAnimation,
        onOpenAnimationComplete: () {
          setState(() {
            _isOpeningAnimation = false;
          });
        },
        isClosingAnimation: _isClosingAnimation,
        onCloseAnimationComplete: () {
          setState(() {
            _isClosingAnimation = false;
          });

          /// Add navigation call based on your navigation setup.
          /// This one is tested only on Android and iOS.
          if (Navigator.canPop(context)) {
            Navigator.pop(context);
          } else {
            SystemNavigator.pop();
          }
        },
        iconSize: 48.0,
        actionIcons: [
          TappableIconData(
            assetPath: 'assets/images/local_florist.svg',
            color: Colors.green,
            tappedColor: Colors.grey,
            onTap: () {
              /// Add navigation call based on your navigation setup.
            },
            outerBorderColor: Colors.white,
            outerBorderSize: 10,
            innerBorderColor: Colors.white,
          ),
          TappableIconData(
            assetPath: 'assets/images/local_activity.svg',
            color: Colors.purple,
            tappedColor: Colors.grey,
            onTap: () {
              /// Add navigation call based on your navigation setup.
            },
            outerBorderColor: Colors.white,
            outerBorderSize: 10,
            innerBorderColor: Colors.white,
          ),
          TappableIconData(
            assetPath: 'assets/images/restaurant.svg',
            color: Colors.orange.shade700,
            tappedColor: Colors.grey,
            onTap: () {
              /// Add navigation call based on your navigation setup.
            },
            outerBorderColor: Colors.white,
            outerBorderSize: 10,
            innerBorderColor: Colors.white,
          ),
          TappableIconData(
            assetPath: 'assets/images/baby_changing_station.svg',
            color: Colors.red.shade700,
            tappedColor: Colors.grey,
            onTap: () {
              /// Add navigation call based on your navigation setup.
            },
            outerBorderColor: Colors.white,
            outerBorderSize: 10,
            innerBorderColor: Colors.white,
          ),
          TappableIconData(
            assetPath: 'assets/images/construction.svg',
            color: Colors.yellow.shade800,
            tappedColor: Colors.grey,
            onTap: () {
              /// Add navigation call based on your navigation setup.
            },
            outerBorderColor: Colors.white,
            outerBorderSize: 10,
            innerBorderColor: Colors.white,
          ),
        ],
        closeIcon: TappableIconData(
          color: const Color(0xFF3678D0),
          assetPath: 'assets/images/close.svg',
          tappedColor: const Color(0xFF3678D0).withOpacity(0.5),
          onTap: () {
            setState(() {
              _isClosingAnimation = true;
            });
          },
          outerBorderColor: Colors.white54,
          outerBorderSize: 12,
          innerBorderColor: Colors.white,
        ),
      ),
      child: CircleNavigator(),
    );
  }
}

Refer to the example to learn how to place a widget in the center of the screen and explore the capabilities of CircleNavigatorConfig.

Additional information

If you find a package relevant or have learned something from it, please do not hesitate to contact me or like it 🙂 Also, please raise any issues you have here on GitHub

I have found two issues that may impact your use of the package:

  • If the animationDuration and backgroundAnimationDuration parameters represent different time values, the onCloseAnimationComplete function is called twice. Usually, the first call causes the app to leave the screen, which is fine.
  • Some users may want to turn off animations entirely. If it applies to you, please let me know so that I can improve the package.

Dependencies

I tried to keep the project as minimalistic as possible. So its pubspec keeps only:

  equatable: ^2.0.5
  flutter_svg: ^2.0.2

Related posts:

  1. Advanced sqflite Techniques in Flutter: Error handling
  2. Advanced sqflite Techniques in Flutter: Batch, Migrations, Transactions, Debugging
  3. Open Ai in Flutter: Step by Step building a basic language translation application with Text completion