company_branded_loader 0.0.2
company_branded_loader: ^0.0.2 copied to clipboard
A customizable Flutter branded loader overlay with animated company text, canvas effects, blur, and dynamic API status support.
import 'package:company_branded_loader/company_branded_loader.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const BrandedLoaderExampleApp());
}
class BrandedLoaderExampleApp extends StatelessWidget {
const BrandedLoaderExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF00A6C7),
brightness: Brightness.light,
),
scaffoldBackgroundColor: const Color(0xFFF4F8FC),
appBarTheme: const AppBarTheme(centerTitle: true),
useMaterial3: true,
),
home: const ExampleHomeScreen(),
);
}
}
enum LoaderDemoMode { basic, dynamic, custom }
class ExampleHomeScreen extends StatefulWidget {
const ExampleHomeScreen({super.key});
@override
State<ExampleHomeScreen> createState() => _ExampleHomeScreenState();
}
class _ExampleHomeScreenState extends State<ExampleHomeScreen> {
bool isLoading = false;
String? statusText;
LoaderDemoMode mode = LoaderDemoMode.dynamic;
Future<void> simulateApiCall() async {
final useStatusFlow = mode != LoaderDemoMode.basic;
setState(() {
isLoading = true;
statusText = useStatusFlow ? 'Fetching data' : null;
});
await Future<void>.delayed(const Duration(seconds: 1));
if (!mounted) {
return;
}
if (useStatusFlow) {
setState(() {
statusText = 'Processing request';
});
}
await Future<void>.delayed(const Duration(seconds: 1));
if (!mounted) {
return;
}
if (useStatusFlow) {
setState(() {
statusText = 'Finalizing';
});
}
await Future<void>.delayed(const Duration(seconds: 1));
if (!mounted) {
return;
}
setState(() {
isLoading = false;
statusText = null;
});
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final accentColor = switch (mode) {
LoaderDemoMode.basic => const Color(0xFF00E5FF),
LoaderDemoMode.dynamic => const Color(0xFF36CFFF),
LoaderDemoMode.custom => const Color(0xFFFF7A59),
};
return CompanyBrandedLoaderOverlay(
isLoading: isLoading,
companyName: 'CoreDron',
dynamicStatusText: statusText,
accentColor: accentColor,
customAnimation: mode == LoaderDemoMode.custom
? _ExampleCustomAnimation(accentColor: accentColor)
: null,
child: Scaffold(
appBar: AppBar(title: const Text('Branded Loader Example')),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(28),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.primary.withValues(alpha: 0.12),
colorScheme.secondary.withValues(alpha: 0.08),
],
),
border: Border.all(
color: Colors.white.withValues(alpha: 0.7),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Premium Loader Modes',
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.w700),
),
const SizedBox(height: 8),
Text(
'Switch between a simple brand overlay, dynamic API statuses, and a custom animation slot.',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: const Color(0xFF4B5563),
),
),
const SizedBox(height: 18),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
_ModeChip(
label: 'Basic',
selected: mode == LoaderDemoMode.basic,
onTap: () => setState(() {
mode = LoaderDemoMode.basic;
}),
),
_ModeChip(
label: 'Dynamic Status',
selected: mode == LoaderDemoMode.dynamic,
onTap: () => setState(() {
mode = LoaderDemoMode.dynamic;
}),
),
_ModeChip(
label: 'Custom Animation',
selected: mode == LoaderDemoMode.custom,
onTap: () => setState(() {
mode = LoaderDemoMode.custom;
}),
),
],
),
],
),
),
const SizedBox(height: 20),
Expanded(
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.06),
blurRadius: 28,
offset: const Offset(0, 16),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Current Configuration',
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(fontWeight: FontWeight.w700),
),
const SizedBox(height: 14),
Text(
switch (mode) {
LoaderDemoMode.basic =>
'Shows the premium default loader with the built-in orbit animation.',
LoaderDemoMode.dynamic =>
'Adds staged API messages: Fetching data, Processing request, then Finalizing.',
LoaderDemoMode.custom =>
'Replaces the canvas loader with a custom animation widget while keeping the same overlay shell.',
},
style:
Theme.of(context).textTheme.bodyLarge?.copyWith(
color: const Color(0xFF4B5563),
height: 1.45,
),
),
const Spacer(),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: simulateApiCall,
style: FilledButton.styleFrom(
backgroundColor: accentColor,
foregroundColor: const Color(0xFF04131A),
padding: const EdgeInsets.symmetric(vertical: 18),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
child: const Text('Start API Call'),
),
),
],
),
),
),
],
),
),
),
),
);
}
}
class _ModeChip extends StatelessWidget {
const _ModeChip({
required this.label,
required this.selected,
required this.onTap,
});
final String label;
final bool selected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(999),
child: AnimatedContainer(
duration: const Duration(milliseconds: 220),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: selected
? const Color(0xFF071A22)
: const Color(0xFFFFFFFF).withValues(alpha: 0.72),
borderRadius: BorderRadius.circular(999),
border: Border.all(
color: selected
? Colors.white.withValues(alpha: 0.14)
: const Color(0xFFD7E4F2),
),
),
child: Text(
label,
style: TextStyle(
color: selected ? Colors.white : const Color(0xFF24323D),
fontWeight: FontWeight.w600,
),
),
),
);
}
}
class _ExampleCustomAnimation extends StatefulWidget {
const _ExampleCustomAnimation({required this.accentColor});
final Color accentColor;
@override
State<_ExampleCustomAnimation> createState() =>
_ExampleCustomAnimationState();
}
class _ExampleCustomAnimationState extends State<_ExampleCustomAnimation>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1400),
)..repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox.square(
dimension: 96,
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
final t = Curves.easeInOut.transform(_controller.value);
return Transform.scale(
scale: 0.92 + (0.1 * t),
child: DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
widget.accentColor.withValues(alpha: 0.9),
widget.accentColor.withValues(alpha: 0.14),
],
),
boxShadow: [
BoxShadow(
color: widget.accentColor.withValues(alpha: 0.35),
blurRadius: 24,
spreadRadius: 3,
),
],
),
child: Center(
child: Icon(
Icons.auto_awesome_rounded,
size: 40,
color: Colors.white.withValues(alpha: 0.96),
),
),
),
);
},
),
);
}
}