Concentric Animation Onboarding
Loading preview
Starting the Flutter app
A Flutter onboarding flow with animated concentric color transitions between slides.
import 'package:concentric_transition/concentric_transition.dart';
import 'package:flutter/material.dart';
final pages = [
const PageData(
icon: Icons.food_bank_outlined,
title: "Search for your favourite food",
bgColor: Color(0xff3b1791),
textColor: Colors.white,
),
const PageData(
icon: Icons.shopping_bag_outlined,
title: "Add it to cart",
bgColor: Color(0xfffab800),
textColor: Color(0xff3b1790),
),
const PageData(
icon: Icons.delivery_dining,
title: "Order and wait",
bgColor: Color(0xffffffff),
textColor: Color(0xff3b1790),
),
];
class ConcentricAnimationOnboarding extends StatelessWidget {
const ConcentricAnimationOnboarding({super.key});
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
body: ConcentricPageView(
colors: pages.map((p) => p.bgColor).toList(),
radius: screenWidth * 0.1,
nextButtonBuilder: (context) => Padding(
padding: const EdgeInsets.only(left: 3), // visual center
child: Icon(Icons.navigate_next, size: screenWidth * 0.08),
),
// enable itemcount to disable infinite scroll
// itemCount: pages.length,
// opacityFactor: 2.0,
scaleFactor: 2,
// verticalPosition: 0.7,
// direction: Axis.vertical,
// itemCount: pages.length,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (index) {
final page = pages[index % pages.length];
return SafeArea(child: _Page(page: page));
},
),
);
}
}
class PageData {
final String? title;
final IconData? icon;
final Color bgColor;
final Color textColor;
const PageData({
this.title,
this.icon,
this.bgColor = Colors.white,
this.textColor = Colors.black,
});
}
class _Page extends StatelessWidget {
final PageData page;
const _Page({required this.page});
@override
Widget build(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: page.textColor,
),
child: Icon(page.icon, size: screenHeight * 0.1, color: page.bgColor),
),
Text(
page.title ?? "",
style: TextStyle(
color: page.textColor,
fontSize: screenHeight * 0.035,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
],
);
}
}
Add it to your project
Copies the Dart source into your app, so you own it. Also adds concentric_transition to your pubspec.
First time? Run dart pub global activate fluttertemplates_dev, or read the CLI guide.
How it works
Built on the concentric_transition package: ConcentricPageView supplies the expanding circle wipe, taking its colors from pages. Since itemCount is commented out, the carousel loops forever, with index % pages.length mapping back to the three PageData entries. There’s no finish callback here. _Page scales icon and text from screen height, inverting bgColor and textColor for the icon badge.
Adapting it
- Uncomment
itemCountto stop the looping, then add a route to your home screen after the last page. - Edit
pagesfor your own copy, colors and icons. - Try the other commented options, such as
direction.