changelog_flutter_kit 1.1.1
changelog_flutter_kit: ^1.1.1 copied to clipboard
A headless and platform-adaptive 'What's New' and release notes kit for Flutter apps with native iOS Cupertino, Material 3, SemVer comparison, and persistence.
import 'package:flutter/material.dart';
import 'package:changelog_flutter_kit/changelog_flutter_kit.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const WhatsNewDemoApp());
}
class WhatsNewDemoApp extends StatelessWidget {
const WhatsNewDemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "What's New Showcase",
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.indigo,
brightness: Brightness.light,
),
darkTheme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.indigo,
brightness: Brightness.dark,
),
home: const DemoHomePage(),
);
}
}
class DemoHomePage extends StatefulWidget {
const DemoHomePage({super.key});
@override
State<DemoHomePage> createState() => _DemoHomePageState();
}
class _DemoHomePageState extends State<DemoHomePage> {
late final InMemoryWhatsNewStorage _storage;
late final WhatsNewController _controller;
String _simulatedVersion = '2.1.0';
final List<VersionChangelog> _sampleChangelogs = [
const VersionChangelog(
version: '2.1.0',
title: 'Performance & Polish',
releaseDate: 'October 2026',
entries: [
ChangelogEntry(
type: ChangeType.feature,
title: 'Adaptive Cupertino & Material UI',
text:
'Sheets now seamlessly match iOS HIG and Android Material 3 specs.',
iconOverride: Icons.auto_awesome,
),
ChangelogEntry(
type: ChangeType.feature,
title: 'Headless Mode',
text:
'Full headless widgets and controller for custom-designed release notes.',
iconOverride: Icons.developer_mode,
),
ChangelogEntry(
type: ChangeType.pro,
title: 'Cloud Realtime Sync',
text:
'Sync release settings and announcements across all user devices.',
isPro: true,
iconOverride: Icons.cloud_sync_rounded,
),
ChangelogEntry(
type: ChangeType.improvement,
title: 'SemVer 2.0 Comparator',
text: 'Proper precedence logic for prerelease and build tags.',
iconOverride: Icons.speed_rounded,
),
ChangelogEntry(
type: ChangeType.fix,
title: 'Memory Optimization',
text: 'Eliminated unnecessary rebuilds in sheet list views.',
iconOverride: Icons.bug_report_rounded,
),
],
),
const VersionChangelog(
version: '2.0.0',
title: 'Major 2.0 Redesign',
releaseDate: 'September 2026',
entries: [
ChangelogEntry(
type: ChangeType.feature,
title: 'All-new Design Language',
text:
'Polished layouts, dark mode optimization, and micro-interactions.',
iconOverride: Icons.palette_outlined,
),
ChangelogEntry(
type: ChangeType.breaking,
title: 'Updated Controller API',
text: 'Async storage provider replaced legacy synchronous calls.',
iconOverride: Icons.warning_amber_rounded,
),
],
),
const VersionChangelog(
version: '1.0.0',
title: 'Initial Release',
releaseDate: 'January 2026',
entries: [
ChangelogEntry(
type: ChangeType.feature,
title: 'Welcome to the App',
text: 'First public version with changelog sheet support.',
iconOverride: Icons.rocket_launch_rounded,
),
],
),
];
@override
void initState() {
super.initState();
_storage = InMemoryWhatsNewStorage(initialLastSeen: '2.0.0');
_initController();
}
void _initController() {
_controller = WhatsNewController(
changelogs: _sampleChangelogs,
storage: _storage,
currentVersionProvider: () async => _simulatedVersion,
);
}
void _showPaywallModal(ChangelogEntry entry) {
showModalBottomSheet<void>(
context: context,
builder: (ctx) => Container(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Icon(Icons.workspace_premium, size: 48, color: Colors.amber),
const SizedBox(height: 12),
Text(
'Unlock ${entry.title ?? "Pro Feature"}',
style: Theme.of(ctx).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
entry.text,
textAlign: TextAlign.center,
style: Theme.of(ctx).textTheme.bodyMedium,
),
const SizedBox(height: 20),
FilledButton(
onPressed: () => Navigator.pop(ctx),
child: const Text(r'Upgrade to Pro ($4.99/mo)'),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("What's New Showcase"),
actions: [
IconButton(
tooltip: 'View Full History',
icon: const Icon(Icons.history_rounded),
onPressed: () {
WhatsNewSheet.showHistory(
context,
changelogs: _sampleChangelogs,
isProUser: false,
onProTap: _showPaywallModal,
);
},
),
],
),
body: ListView(
padding: const EdgeInsets.all(20),
children: [
// -----------------------------------------------------------------
// Headless Embedded Showcase
// -----------------------------------------------------------------
Text(
'HEADLESS MODE (WhatsNewBuilder)',
style: Theme.of(context).textTheme.labelLarge?.copyWith(
letterSpacing: 1.1,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 8),
WhatsNewBuilder(
controller: _controller,
builder: (context, state) {
if (state.isLoading) {
return const Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Center(child: CircularProgressIndicator()),
),
);
}
if (!state.hasUpdate) {
return Card(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
child: const Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.check_circle_outline, color: Colors.green),
SizedBox(width: 12),
Expanded(
child: Text(
'All caught up! No unread changelogs for this version.',
),
),
],
),
),
);
}
final changelog = state.currentChangelog!;
return Card(
elevation: 3,
color: Theme.of(context).colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.celebration_rounded,
color: Colors.deepPurple),
const SizedBox(width: 8),
Expanded(
child: Text(
'Custom UI Banner: v${changelog.version} — ${changelog.title ?? ""}',
style:
const TextStyle(fontWeight: FontWeight.bold),
),
),
IconButton(
icon: const Icon(Icons.close_rounded),
onPressed: state.markCurrentSeen,
),
],
),
const SizedBox(height: 8),
Text(
'This entire card is rendered headlessly with developer-defined widgets!',
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: state.markCurrentSeen,
icon: const Icon(Icons.done_all_rounded, size: 18),
label: const Text('Mark Seen (Headless API)'),
),
],
),
),
);
},
),
const SizedBox(height: 32),
// -----------------------------------------------------------------
// Headful Presentation Options
// -----------------------------------------------------------------
Text(
'HEADFUL PRESENTATIONS',
style: Theme.of(context).textTheme.labelLarge?.copyWith(
letterSpacing: 1.1,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 12),
// iOS Cupertino Style
Card(
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.blueAccent,
child: Icon(Icons.phone_iphone_rounded, color: Colors.white),
),
title: const Text('iOS Cupertino Sheet'),
subtitle: const Text(
'Cupertino typography, badges, and action buttons'),
trailing: const Icon(Icons.arrow_forward_ios_rounded, size: 16),
onTap: () {
WhatsNewSheet.show(
context,
changelog: _sampleChangelogs.first,
onGotIt: () async {},
isProUser: false,
onProTap: _showPaywallModal,
presentationStyle: WhatsNewPresentationStyle.cupertino,
);
},
),
),
// Android Material 3 Style
Card(
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.teal,
child: Icon(Icons.android_rounded, color: Colors.white),
),
title: const Text('Android Material 3 Sheet'),
subtitle:
const Text('M3 surface elevation, pills, and FilledButton'),
trailing: const Icon(Icons.arrow_forward_ios_rounded, size: 16),
onTap: () {
WhatsNewSheet.show(
context,
changelog: _sampleChangelogs.first,
onGotIt: () async {},
isProUser: false,
onProTap: _showPaywallModal,
presentationStyle: WhatsNewPresentationStyle.material,
);
},
),
),
// Dialog Style
Card(
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.purple,
child: Icon(Icons.desktop_mac_rounded, color: Colors.white),
),
title: const Text('Centered Modal Dialog'),
subtitle:
const Text('Ideal for Desktop, Web, and Tablet layouts'),
trailing: const Icon(Icons.arrow_forward_ios_rounded, size: 16),
onTap: () {
WhatsNewSheet.show(
context,
changelog: _sampleChangelogs.first,
onGotIt: () async {},
isProUser: true,
presentationStyle: WhatsNewPresentationStyle.dialog,
);
},
),
),
// Full History
Card(
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.deepOrange,
child: Icon(Icons.library_books_rounded, color: Colors.white),
),
title: const Text('Version History Page'),
subtitle:
const Text('Full scrollable changelog archive for Settings'),
trailing: const Icon(Icons.arrow_forward_ios_rounded, size: 16),
onTap: () {
WhatsNewSheet.showHistory(
context,
changelogs: _sampleChangelogs,
isProUser: false,
onProTap: _showPaywallModal,
);
},
),
),
const SizedBox(height: 32),
// -----------------------------------------------------------------
// Simulator & Debug Tools
// -----------------------------------------------------------------
Text(
'TESTING & SIMULATION',
style: Theme.of(context).textTheme.labelLarge?.copyWith(
letterSpacing: 1.1,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 12),
FilledButton.tonalIcon(
onPressed: () async {
final messenger = ScaffoldMessenger.of(context);
await _storage.clear();
if (!mounted) return;
setState(() {
_simulatedVersion = '2.1.0';
_initController();
});
messenger.showSnackBar(
const SnackBar(
content: Text('Reset seen state to fresh install!'),
),
);
},
icon: const Icon(Icons.restart_alt_rounded),
label: const Text('Reset Storage (Simulate Fresh Install)'),
),
],
),
);
}
}