masterfabric_flutter_quick_actions 0.0.4 copy "masterfabric_flutter_quick_actions: ^0.0.4" to clipboard
masterfabric_flutter_quick_actions: ^0.0.4 copied to clipboard

A Flutter plugin for iOS Quick Actions and Android App Shortcuts

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:masterfabric_flutter_quick_actions/masterfabric_flutter_quick_actions.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:io';

// Initial quick actions for first boot
final List<QuickAction> _initialQuickActions = [
  QuickAction(
    type: 'search',
    title: 'Search',
    subtitle: 'Find items quickly',
    iconType: 'UIApplicationShortcutIconTypeSearch',
    iconName: 'ic_search',
  ),
  QuickAction(
    type: 'compose',
    title: 'New Message',
    subtitle: 'Start a conversation',
    iconType: 'UIApplicationShortcutIconTypeCompose',
    iconName: 'ic_compose',
  ),
  QuickAction(
    type: 'favorite',
    title: 'Favorites',
    subtitle: 'View your favorites',
    iconType: 'UIApplicationShortcutIconTypeFavorite',
    iconName: 'ic_favorite',
  ),
  QuickAction(
    type: 'share',
    title: 'Share',
    subtitle: 'Share with friends',
    iconType: 'UIApplicationShortcutIconTypeShare',
    iconName: 'ic_share',
  ),
];

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize quick actions with initial actions
  await QuickActionsManager.initialize(
    actions: _initialQuickActions,
    onAction: (action) {
      // Handle quick action - will be handled in the widget
      debugPrint('Quick action triggered: ${action.type}');
    },
  );

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MF Quick Actions',
      theme: ThemeData(
        scaffoldBackgroundColor: Colors.white,
        colorScheme: const ColorScheme.light(
          primary: Colors.black,
          onPrimary: Colors.white,
          surface: Colors.white,
          onSurface: Colors.black,
        ),
        appBarTheme: const AppBarTheme(
          backgroundColor: Colors.white,
          foregroundColor: Colors.black,
          elevation: 0,
          centerTitle: true,
        ),
        textButtonTheme: TextButtonThemeData(
          style: TextButton.styleFrom(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(7),
            ),
          ),
        ),
        dialogTheme: DialogThemeData(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(7),
          ),
        ),
        snackBarTheme: SnackBarThemeData(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(7),
          ),
        ),
        textTheme: const TextTheme(
          displayLarge: TextStyle(
            fontSize: 32,
            fontWeight: FontWeight.w300,
            color: Colors.black,
            letterSpacing: -0.5,
          ),
          displayMedium: TextStyle(
            fontSize: 28,
            fontWeight: FontWeight.w400,
            color: Colors.black,
          ),
          displaySmall: TextStyle(
            fontSize: 24,
            fontWeight: FontWeight.w400,
            color: Colors.black,
          ),
          headlineMedium: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.w500,
            color: Colors.black,
          ),
          bodyLarge: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w400,
            color: Colors.black87,
            height: 1.5,
          ),
          bodyMedium: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w400,
            color: Colors.black87,
            height: 1.5,
          ),
        ),
        useMaterial3: true,
      ),
      home: const QuickActionsDemo(),
    );
  }
}

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

  @override
  State<QuickActionsDemo> createState() => _QuickActionsDemoState();
}

class _QuickActionsDemoState extends State<QuickActionsDemo> {
  String _lastAction = 'No action triggered yet';
  String _launchAction = 'No launch action detected';
  int _actionCount = 0;
  QuickAction? _currentAction;

  @override
  void initState() {
    super.initState();
    _restoreLaunchAction();
    _setupActionListener();
  }

  Future<void> _restoreLaunchAction() async {
    // Check if app was launched via quick action (home restore)
    final launchAction = await QuickActionsManager.getLaunchAction();
    if (launchAction != null && mounted) {
      setState(() {
        _launchAction = '${launchAction.type}: ${launchAction.title}';
        _currentAction = launchAction;
        _actionCount++;
      });
      _showActionDetails(launchAction, isLaunchAction: true);
    } else if (mounted) {
      setState(() {
        _launchAction = 'App launched normally';
      });
    }
  }

  void _setupActionListener() {
    // Listen to quick action stream for actions triggered while app is running
    QuickActionsManager.actionStream.listen((action) {
      if (mounted) {
        setState(() {
          _lastAction = '${action.type}: ${action.title}';
          _currentAction = action;
          _actionCount++;
        });
        _showActionDetails(action, isLaunchAction: false);
      }
    });
  }

  void _showActionDetails(QuickAction action, {required bool isLaunchAction}) {
    showDialog(
      context: context,
      builder: (context) => Dialog(
        backgroundColor: Colors.white,
        child: Padding(
          padding: const EdgeInsets.all(24.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                isLaunchAction ? 'App Launched Via Quick Action' : 'Quick Action Triggered',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
              const SizedBox(height: 24),
              _buildDetailRow('Type', action.type),
              const SizedBox(height: 12),
              _buildDetailRow('Title', action.title),
              if (action.subtitle != null) ...[
                const SizedBox(height: 12),
                _buildDetailRow('Subtitle', action.subtitle!),
              ],
              if (action.userInfo != null) ...[
                const SizedBox(height: 12),
                _buildDetailRow('User Info', action.userInfo.toString()),
              ],
              const SizedBox(height: 24),
              SizedBox(
                width: double.infinity,
                child: TextButton(
                  onPressed: () => Navigator.of(context).pop(),
                  style: TextButton.styleFrom(
                    foregroundColor: Colors.black,
                    side: const BorderSide(color: Colors.black),
                    padding: const EdgeInsets.symmetric(vertical: 16),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(7),
                    ),
                  ),
                  child: const Text('Close'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildDetailRow(String label, String value) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          label,
          style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                fontWeight: FontWeight.w500,
                color: Colors.black54,
              ),
        ),
        const SizedBox(height: 4),
        Text(
          value,
          style: Theme.of(context).textTheme.bodyLarge,
        ),
      ],
    );
  }

  Future<void> _updateActions() async {
    await QuickActionsManager.setActions([
      QuickAction(
        type: 'updated_action',
        title: 'Updated Action',
        subtitle: 'This was updated dynamically',
        iconType: 'UIApplicationShortcutIconTypeUpdate',
        iconName: 'ic_update',
      ),
    ]);
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: const Text('Quick actions updated'),
          backgroundColor: Colors.black87,
          behavior: SnackBarBehavior.floating,
        ),
      );
    }
  }

  Future<void> _clearActions() async {
    await QuickActionsManager.clearActions();
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: const Text('Quick actions cleared'),
          backgroundColor: Colors.black87,
          behavior: SnackBarBehavior.floating,
        ),
      );
    }
  }

  Future<void> _restoreInitialActions() async {
    await QuickActionsManager.setActions(_initialQuickActions);
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: const Text('Quick actions restored to initial state'),
          backgroundColor: Colors.black87,
          behavior: SnackBarBehavior.floating,
        ),
      );
    }
  }

  Future<void> _requestPinShortcut() async {
    if (Platform.isAndroid) {
      final success = await QuickActionsManager.requestPinShortcut(
        QuickAction(
          type: 'pinned_feature',
          title: 'Pinned Feature',
          subtitle: 'Quick access',
          iconName: 'ic_pin',
        ),
      );
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(success
                ? 'Shortcut pinned successfully'
                : 'Failed to pin shortcut'),
            backgroundColor: Colors.black87,
            behavior: SnackBarBehavior.floating,
          ),
        );
      }
    } else {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: const Text('Pinned shortcuts are only available on Android'),
            backgroundColor: Colors.black87,
            behavior: SnackBarBehavior.floating,
          ),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text('MF Quick Actions'),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'A Flutter plugin for iOS Quick Actions and Android App Shortcuts',
              style: Theme.of(context).textTheme.bodyLarge?.copyWith(
                    color: Colors.black54,
                  ),
            ),
            const SizedBox(height: 4),
            GestureDetector(
              onTap: () async {
                final url = Uri.parse('https://github.com/gurkanfikretgunak/masterfabric_flutter_quick_actions');
                if (await canLaunchUrl(url)) {
                  await launchUrl(url, mode: LaunchMode.externalApplication);
                }
              },
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Padding(
                    padding: const EdgeInsets.only(top: 2),
                    child: Icon(
                      Icons.link,
                      size: 14,
                      color: Colors.black54,
                    ),
                  ),
                  const SizedBox(width: 4),
                  Expanded(
                    child: Text(
                      'Repository: github.com/gurkanfikretgunak/masterfabric_flutter_quick_actions',
                      style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                            color: Colors.black54,
                            fontSize: 12,
                            decoration: TextDecoration.underline,
                            decorationColor: Colors.black54,
                          ),
                      softWrap: true,
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 48),
            _buildDetailedSection(
              'Launch Action',
              _launchAction,
              'The action that launched the app (if any)',
              Icons.launch,
            ),
            const SizedBox(height: 32),
            _buildDetailedSection(
              'Last Action',
              _lastAction,
              'Most recent quick action triggered',
              Icons.touch_app,
            ),
            const SizedBox(height: 32),
            _buildDetailedSection(
              'Action Count',
              '$_actionCount',
              'Total number of actions triggered',
              Icons.numbers,
            ),
            const SizedBox(height: 48),
            Row(
              children: [
                Icon(
                  Icons.settings,
                  size: 20,
                  color: Colors.black87,
                ),
                const SizedBox(width: 8),
                Text(
                  'Actions',
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
              ],
            ),
            const SizedBox(height: 16),
            _buildTextButton(
              'Restore Initial Actions',
              _restoreInitialActions,
              subtitle: 'Reset to first boot state',
            ),
            const SizedBox(height: 12),
            _buildTextButton(
              'Update Actions Dynamically',
              _updateActions,
            ),
            const SizedBox(height: 12),
            _buildTextButton(
              'Clear Actions',
              _clearActions,
            ),
            const SizedBox(height: 12),
            _buildTextButton(
              'Request Pin Shortcut',
              _requestPinShortcut,
              subtitle: Platform.isAndroid ? null : 'Android only',
            ),
            const SizedBox(height: 48),
            _buildDetailedSection(
              'Instructions',
              '1. Long-press the app icon on your home screen\n'
              '2. You should see 4 quick actions\n'
              '3. Tap any action to trigger it\n'
              '4. The action details will be displayed',
              null,
              Icons.info_outline,
            ),
            const SizedBox(height: 32),
            _buildDetailedSection(
              'Platform Information',
              Platform.isIOS
                  ? 'iOS: Quick actions use system icon types. Supports iOS 12.0+ with 3D Touch or Haptic Touch.'
                  : Platform.isAndroid
                      ? 'Android: App shortcuts use drawable resources. Requires API level 25+ (Android 7.1+). Pinned shortcuts available on Android 8.0+.'
                      : 'Quick actions are only available on iOS and Android platforms.',
              null,
              Platform.isIOS ? Icons.phone_iphone : Platform.isAndroid ? Icons.phone_android : Icons.devices,
            ),
            if (_currentAction != null) ...[
              const SizedBox(height: 48),
              Container(
                padding: const EdgeInsets.all(20),
                decoration: BoxDecoration(
                  color: Colors.black.withValues(alpha: 0.03),
                  borderRadius: BorderRadius.circular(7),
                  border: Border.all(color: Colors.black.withValues(alpha: 0.1), width: 1),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Icon(
                          Icons.details,
                          size: 20,
                          color: Colors.black87,
                        ),
                        const SizedBox(width: 8),
                        Text(
                          'Current Action Details',
                          style: Theme.of(context).textTheme.headlineMedium,
                        ),
                      ],
                    ),
                    const SizedBox(height: 16),
                    _buildDetailRow('Type', _currentAction!.type),
                    const SizedBox(height: 12),
                    _buildDetailRow('Title', _currentAction!.title),
                    if (_currentAction!.subtitle != null) ...[
                      const SizedBox(height: 12),
                      _buildDetailRow('Subtitle', _currentAction!.subtitle!),
                    ],
                    if (_currentAction!.iconType != null) ...[
                      const SizedBox(height: 12),
                      _buildDetailRow('Icon Type (iOS)', _currentAction!.iconType!),
                    ],
                    if (_currentAction!.iconName != null) ...[
                      const SizedBox(height: 12),
                      _buildDetailRow('Icon Name (Android)', _currentAction!.iconName!),
                    ],
                    if (_currentAction!.userInfo != null) ...[
                      const SizedBox(height: 12),
                      _buildDetailRow('User Info', _currentAction!.userInfo.toString()),
                    ],
                  ],
                ),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildDetailedSection(
    String title,
    String content,
    String? subtitle,
    IconData? icon,
  ) {
    return Container(
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(7),
        border: Border.all(color: Colors.black.withValues(alpha: 0.1), width: 1),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              if (icon != null) ...[
                Icon(
                  icon,
                  size: 20,
                  color: Colors.black87,
                ),
                const SizedBox(width: 8),
              ],
              Expanded(
                child: Text(
                  title,
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
              ),
            ],
          ),
          if (subtitle != null) ...[
            const SizedBox(height: 8),
            Text(
              subtitle,
              style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                    color: Colors.black54,
                    fontSize: 13,
                  ),
            ),
          ],
          const SizedBox(height: 12),
          Container(
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: Colors.black.withValues(alpha: 0.02),
              borderRadius: BorderRadius.circular(7),
            ),
            child: Text(
              content,
              style: Theme.of(context).textTheme.bodyLarge?.copyWith(
                    fontWeight: FontWeight.w500,
                  ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildTextButton(String text, VoidCallback onPressed, {String? subtitle}) {
    return SizedBox(
      width: double.infinity,
      child: TextButton(
        onPressed: onPressed,
        style: TextButton.styleFrom(
          foregroundColor: Colors.black,
          side: const BorderSide(color: Colors.black, width: 1),
          padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
          alignment: Alignment.centerLeft,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(7),
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              text,
              style: Theme.of(context).textTheme.bodyLarge,
            ),
            if (subtitle != null) ...[
              const SizedBox(height: 4),
              Text(
                subtitle,
                style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                      color: Colors.black54,
                    ),
              ),
            ],
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    QuickActionsManager.dispose();
    super.dispose();
  }
}
1
likes
150
points
188
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for iOS Quick Actions and Android App Shortcuts

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on masterfabric_flutter_quick_actions

Packages that implement masterfabric_flutter_quick_actions