snappy_snag 0.0.3 copy "snappy_snag: ^0.0.3" to clipboard
snappy_snag: ^0.0.3 copied to clipboard

An easy-to-use in-app bug reporting and user feedback SDK for Flutter, allowing users to send screenshots, device info, and custom logs seamlessly.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:snappy_snag/snappy_snag.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize SnappySnag SDK in Sandbox / Demo Mode
  // No server configuration or API key required to test locally!
  SnappySnag().initialize(
    apiKey: 'demo', // Use 'demo' for instant local testing without an account
    showTriggerButton: true, // Show the floating SnappySnag trigger button
    enableLogging: true,
    user: const SnappySnagUser(
      id: 'demo_user_1',
      name: 'Flutter Explorer',
      email: 'demo@example.com',
    ),
  );

  runApp(const SnappySnagShowroomApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SnappySnag Demo Showcase',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF6366F1),
          brightness: Brightness.dark,
        ),
        scaffoldBackgroundColor: const Color(0xFF0F172A),
        useMaterial3: true,
      ),
      // Pass SnappySnag default navigator key
      navigatorKey: SnappySnag.defaultNavigatorKey,
      // Wrap your app with SnappySnagOverlay
      builder: (context, child) => SnappySnagOverlay(
        child: child ?? const SizedBox.shrink(),
      ),
      home: const ShowroomHomePage(),
    );
  }
}

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

  @override
  State<ShowroomHomePage> createState() => _ShowroomHomePageState();
}

class _ShowroomHomePageState extends State<ShowroomHomePage> {
  int _counter = 0;
  bool _toggleSwitch = true;
  double _sliderValue = 40.0;
  final TextEditingController _feedbackInput = TextEditingController(
    text: 'Example issue: The checkout button has incorrect padding.',
  );

  @override
  void dispose() {
    _feedbackInput.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color(0xFF1E293B),
        title: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(6),
              decoration: BoxDecoration(
                color: const Color(0xFF6366F1).withOpacity(0.2),
                borderRadius: BorderRadius.circular(8),
              ),
              child: const Icon(Icons.bolt, color: Color(0xFF818CF8), size: 20),
            ),
            const SizedBox(width: 10),
            const Text(
              'SnappySnag Playground',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
          ],
        ),
        actions: [
          IconButton(
            tooltip: 'Launch Feedback Mode',
            icon: const Icon(Icons.camera_alt_outlined),
            onPressed: () => SnappySnag.startFeedbackMode(context: context),
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Banner: Demo Mode Announcement
            Container(
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                gradient: const LinearGradient(
                  colors: [Color(0xFF312E81), Color(0xFF1E1B4B)],
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                ),
                borderRadius: BorderRadius.circular(16),
                border: Border.all(color: const Color(0xFF6366F1).withOpacity(0.4)),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Container(
                        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                        decoration: BoxDecoration(
                          color: const Color(0xFF22C55E).withOpacity(0.2),
                          borderRadius: BorderRadius.circular(6),
                          border: Border.all(color: const Color(0xFF22C55E)),
                        ),
                        child: const Text(
                          'DEMO MODE ACTIVE',
                          style: TextStyle(
                            color: Color(0xFF4ADE80),
                            fontSize: 11,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                      const Spacer(),
                      const Icon(Icons.touch_app_outlined, color: Colors.white70, size: 18),
                    ],
                  ),
                  const SizedBox(height: 12),
                  const Text(
                    'No Account or Server Required!',
                    style: TextStyle(
                      fontSize: 17,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                  const SizedBox(height: 6),
                  const Text(
                    '1. Tap the floating lightning icon on the bottom right (or the button below).\n'
                    '2. Tap anywhere on this screen to drop a Pin with a note.\n'
                    '3. Tap "Send" — you will see the full widget tree and pin payload in your debug console.',
                    style: TextStyle(
                      fontSize: 13,
                      height: 1.5,
                      color: Colors.white70,
                    ),
                  ),
                  const SizedBox(height: 14),
                  ElevatedButton.icon(
                    style: ElevatedButton.styleFrom(
                      backgroundColor: const Color(0xFF6366F1),
                      foregroundColor: Colors.white,
                      elevation: 0,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                    icon: const Icon(Icons.camera_alt),
                    label: const Text('Try Feedback Capture Now'),
                    onPressed: () {
                      SnappySnag.startFeedbackMode(context: context);
                    },
                  ),
                ],
              ),
            ),

            const SizedBox(height: 24),
            const Text(
              'Sample Interactive UI Components',
              style: TextStyle(
                fontSize: 15,
                fontWeight: FontWeight.bold,
                color: Colors.white70,
              ),
            ),
            const SizedBox(height: 12),

            // Card 1: Counter & Buttons
            Card(
              color: const Color(0xFF1E293B),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        const Text('Interactive Counter', style: TextStyle(fontWeight: FontWeight.bold)),
                        const SizedBox(height: 4),
                        Text('Current Value: $_counter', style: const TextStyle(color: Colors.white60, fontSize: 13)),
                      ],
                    ),
                    Row(
                      children: [
                        IconButton.filledTonal(
                          icon: const Icon(Icons.remove),
                          onPressed: () => setState(() => _counter--),
                        ),
                        const SizedBox(width: 8),
                        IconButton.filled(
                          icon: const Icon(Icons.add),
                          onPressed: () => setState(() => _counter++),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 12),

            // Card 2: Switch & Slider
            Card(
              color: const Color(0xFF1E293B),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  children: [
                    SwitchListTile(
                      contentPadding: EdgeInsets.zero,
                      title: const Text('Enable Feature Flag'),
                      subtitle: const Text('Simulates dynamic app state', style: TextStyle(fontSize: 12)),
                      value: _toggleSwitch,
                      onChanged: (val) => setState(() => _toggleSwitch = val),
                    ),
                    const Divider(color: Colors.white12),
                    Row(
                      children: [
                        const Text('Slider: ', style: TextStyle(fontSize: 13)),
                        Expanded(
                          child: Slider(
                            value: _sliderValue,
                            min: 0,
                            max: 100,
                            onChanged: (val) => setState(() => _sliderValue = val),
                          ),
                        ),
                        Text('${_sliderValue.toInt()}%', style: const TextStyle(fontSize: 13, color: Colors.white70)),
                      ],
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 12),

            // Card 3: Sample Text Input Form
            Card(
              color: const Color(0xFF1E293B),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('Sample Input Field', style: TextStyle(fontWeight: FontWeight.bold)),
                    const SizedBox(height: 8),
                    TextField(
                      controller: _feedbackInput,
                      decoration: InputDecoration(
                        filled: true,
                        fillColor: const Color(0xFF0F172A),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(8),
                          borderSide: BorderSide.none,
                        ),
                        hintText: 'Enter some text to test...',
                      ),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 28),

            // Footer note
            Center(
              child: Text(
                'Visit https://snappysnag.com to sync issues with GitHub',
                style: TextStyle(
                  fontSize: 12,
                  color: Colors.white.withOpacity(0.4),
                ),
              ),
            ),
            const SizedBox(height: 40),
          ],
        ),
      ),
    );
  }
}
0
likes
160
points
95
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

An easy-to-use in-app bug reporting and user feedback SDK for Flutter, allowing users to send screenshots, device info, and custom logs seamlessly.

Homepage
Repository (GitHub)
View/report issues

Topics

#bug-reporting #feedback #developer-tools #screenshot #ai

License

MIT (license)

Dependencies

flutter, http, screenshot, sensors_plus, shared_preferences

More

Packages that depend on snappy_snag