segmented_circular_next_button 0.0.1 copy "segmented_circular_next_button: ^0.0.1" to clipboard
segmented_circular_next_button: ^0.0.1 copied to clipboard

A Flutter widget that provides a circular next button with an animated segmented progress ring for onboarding flows

example/lib/main.dart

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

void main() => runApp(const ExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SegmentedCircularNextButton β€” Example',
      debugShowCheckedModeBanner: false,
      home: const OnboardingExample(),
    );
  }
}

class _Page {
  const _Page({
    required this.emoji,
    required this.title,
    required this.subtitle,
    required this.color,
  });
  final String emoji;
  final String title;
  final String subtitle;
  final Color color;
}

const List<_Page> _pages = [
  _Page(
    emoji: 'πŸ—ΊοΈ',
    title: 'Explore the world',
    subtitle: 'Discover places you have never imagined, one swipe at a time.',
    color: Color(0xFF4F6EF7),
  ),
  _Page(
    emoji: '🀝',
    title: 'Meet real people',
    subtitle: 'Connect with locals and fellow travellers wherever you go.',
    color: Color(0xFF2EC4B6),
  ),
  _Page(
    emoji: 'πŸ“Έ',
    title: 'Capture memories',
    subtitle: 'Every moment deserves to be saved beautifully and shared.',
    color: Color(0xFFFF6B6B),
  ),
  _Page(
    emoji: 'πŸš€',
    title: 'Ready to launch',
    subtitle: 'Your adventure starts the second you tap "Get started".',
    color: Color(0xFFFFA552),
  ),
];

class OnboardingExample extends StatefulWidget {
  const OnboardingExample({super.key});

  @override
  State<OnboardingExample> createState() => _OnboardingExampleState();
}

class _OnboardingExampleState extends State<OnboardingExample> {
  late final PageController _controller;
  final ValueNotifier<int> _currentPage = ValueNotifier<int>(0);

  @override
  void initState() {
    super.initState();
    _controller = PageController();
  }

  @override
  void dispose() {
    _controller.dispose();
    _currentPage.dispose();
    super.dispose();
  }

  bool get _isLastPage => _currentPage.value == _pages.length - 1;

  void _next() {
    if (_isLastPage) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('πŸŽ‰  All done! Navigate to home here.')),
      );
      return;
    }
    _controller.nextPage(
      duration: const Duration(milliseconds: 450),
      curve: Curves.easeInOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ValueListenableBuilder<int>(
        valueListenable: _currentPage,
        builder: (context, page, _) {
          final _Page data = _pages[page];
          return AnimatedContainer(
            duration: const Duration(milliseconds: 500),
            curve: Curves.easeInOut,
            color: data.color,
            child: SafeArea(
              child: Column(
                children: [
                  Expanded(
                    child: PageView.builder(
                      controller: _controller,
                      onPageChanged: (i) => _currentPage.value = i,
                      itemCount: _pages.length,
                      itemBuilder: (context, i) =>
                          _PageContent(page: _pages[i]),
                    ),
                  ),

                  SegmentedCircularNextButton(
                    currentPage: page,
                    totalPages: _pages.length,
                    onTap: _next,
                    icon: _isLastPage
                        ? Icons.check_rounded
                        : Icons.arrow_forward_rounded,
                    ringColor: Colors.white,
                    ringBackgroundColor: Colors.white24,
                    buttonColor: Colors.white,
                    iconColor: data.color,
                    ringStrokeWidth: 3,
                    buttonSize: 64,
                    innerButtonSize: 52,
                    animationDuration: const Duration(milliseconds: 450),
                    animationCurve: Curves.easeInOut,
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

class _PageContent extends StatelessWidget {
  const _PageContent({required this.page});
  final _Page page;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 40),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(page.emoji, style: const TextStyle(fontSize: 96)),
          const SizedBox(height: 32),
          Text(
            page.title,
            textAlign: TextAlign.center,
            style: const TextStyle(
              fontSize: 32,
              fontWeight: FontWeight.w700,
              color: Colors.white,
              height: 1.2,
            ),
          ),
          const SizedBox(height: 16),
          Text(
            page.subtitle,
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: 16,
              fontWeight: FontWeight.w400,
              color: Colors.white.withAlpha(150),
              height: 1.6,
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
160
points
7
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter widget that provides a circular next button with an animated segmented progress ring for onboarding flows

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on segmented_circular_next_button