import 'package:app_review/app_review.dart'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( useMaterial3: true, colorSchemeSeed: Colors.blueAccent, brightness: Brightness.light, ), darkTheme: ThemeData( useMaterial3: true, colorSchemeSeed: Colors.blueAccent, brightness: Brightness.dark, ), home: const HomePage(), ); } } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { @override void initState() { super.initState(); AppReview.getAppID.then((onValue) { setState(() { appID = onValue ?? ''; }); log(onValue); }); } String appID = ""; final List _logs = []; void log(String? message) { if (message != null) { setState(() { _logs.insert(0, message); }); debugPrint(message); } } @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( slivers: [ SliverAppBar.large( title: const Text('App Review'), centerTitle: true, ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildInfoCard( context, title: 'App ID', subtitle: appID.isEmpty ? 'Loading...' : appID, icon: Icons.info_outline, onTap: () { AppReview.getAppID.then(log); }, ), const SizedBox(height: 16), Text( 'Actions', style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Theme.of(context).colorScheme.primary, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), _buildActionCard( context, title: 'View Store Page', subtitle: 'Open the app store listing for this app', icon: Icons.storefront, color: Colors.orange, onTap: () async { final id = await AppReview.getIosAppId(); if (id == null || id.isEmpty) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( 'App not found in store. Using Apple Developer App for testing.'), ), ); } // Use Apple Developer App ID for testing // https://apps.apple.com/us/app/apple-developer/id640199958 AppReview.openAppStore( fallbackUrl: 'https://apps.apple.com/us/app/apple-developer/id640199958', ).then(log); } else { AppReview.storeListing.then(log); } }, ), const SizedBox(height: 12), _buildActionCard( context, title: 'Request Review', subtitle: 'Ask the user to rate the app', icon: Icons.star_rate_rounded, color: Colors.amber, onTap: () { AppReview.requestReview.then(log); }, ), const SizedBox(height: 12), _buildActionCard( context, title: 'Write a New Review', subtitle: 'Navigate to the review page', icon: Icons.rate_review_outlined, color: Colors.green, onTap: () async { final id = await AppReview.getIosAppId(); if (id == null || id.isEmpty) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( 'App not found in store. Using Apple Developer App for testing.'), ), ); } // Use Apple Developer App ID for testing AppReview.openIosReview( appId: '640199958', compose: true, ).then(log); } else { AppReview.writeReview.then(log); } }, ), const SizedBox(height: 24), Text( 'Log Output', style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Theme.of(context).colorScheme.primary, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Container( height: 200, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( color: Theme.of(context).colorScheme.outlineVariant, ), ), child: _logs.isEmpty ? Center( child: Text( 'No logs yet', style: TextStyle( color: Theme.of(context).colorScheme.onSurface, ), ), ) : ListView.separated( padding: const EdgeInsets.all(8), itemCount: _logs.length, separatorBuilder: (context, index) => const Divider(height: 1), itemBuilder: (context, index) { return ListTile( dense: true, title: Text( _logs[index], style: TextStyle( fontFamily: 'monospace', fontSize: 12, color: Theme.of(context).colorScheme.onSurface, ), ), leading: const Icon(Icons.code, size: 16), ); }, ), ), ], ), ), ), ], ), ); } Widget _buildInfoCard( BuildContext context, { required String title, required String subtitle, required IconData icon, required VoidCallback onTap, }) { return Card( elevation: 0, color: Theme.of(context).colorScheme.secondaryContainer, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ Icon( icon, color: Theme.of(context).colorScheme.onSecondaryContainer, size: 28, ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.labelLarge?.copyWith( color: Theme.of(context) .colorScheme .onSecondaryContainer .withValues(alpha: 0.8), ), ), const SizedBox(height: 4), Text( subtitle, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Theme.of(context) .colorScheme .onSecondaryContainer, fontWeight: FontWeight.bold, ), ), ], ), ), Icon( Icons.copy, size: 18, color: Theme.of(context) .colorScheme .onSecondaryContainer .withValues(alpha: 0.5), ), ], ), ), ), ); } Widget _buildActionCard( BuildContext context, { required String title, required String subtitle, required IconData icon, required Color color, required VoidCallback onTap, }) { return Card( elevation: 2, shadowColor: Colors.transparent, shape: RoundedRectangleBorder( side: BorderSide( color: Theme.of(context).colorScheme.outlineVariant, ), borderRadius: BorderRadius.circular(12), ), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), shape: BoxShape.circle, ), child: Icon( icon, color: color, size: 24, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.outline, ), ), const SizedBox(height: 2), Text( subtitle, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.outline, ), ), ], ), ), Icon( Icons.chevron_right, color: Theme.of(context).colorScheme.outline, ), ], ), ), ), ); } }