simple_feedback 1.1.2 copy "simple_feedback: ^1.1.2" to clipboard
simple_feedback: ^1.1.2 copied to clipboard

An in-app feedback dialog for Flutter backed by Firebase - type selection, screenshot attachments, automatic page capture, device/user id, and built-in zh/en/ja strings.

example/lib/main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:simple_feedback/simple_feedback.dart';

/// Whether `Firebase.initializeApp()` succeeded. Without a configured
/// Firebase project (google-services.json / GoogleService-Info.plist) the
/// dialog still opens, but submissions will fail with an error snack bar.
var firebaseReady = false;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  try {
    await Firebase.initializeApp();
    firebaseReady = true;
  } catch (e) {
    debugPrint('Firebase not configured: $e');
  }

  // Brand the dialog; every color is optional and follows the app theme
  // by default.
  SimpleFeedback.configure(const SimpleFeedbackConfig(
    collection: 'feedback',
    colors: FeedbackColors(
      primary: Color(0xFFFF8B45),
      primaryContainer: Color(0xFFFFB37E),
      surface: Color(0xFFFAF5EE),
      surfaceLow: Color(0xFFFFF9F2),
    ),
  ));

  runApp(const ExampleApp());
}

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

  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  Locale _locale = const Locale('en');

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'simple_feedback example',
      locale: _locale,
      supportedLocales: const [Locale('en'), Locale('zh'), Locale('ja')],
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      home: DemoPage(
        locale: _locale,
        onLocaleChanged: (locale) => setState(() => _locale = locale),
      ),
    );
  }
}

class DemoPage extends StatefulWidget {
  const DemoPage({
    super.key,
    required this.locale,
    required this.onLocaleChanged,
  });

  final Locale locale;
  final ValueChanged<Locale> onLocaleChanged;

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  final _boundaryKey = GlobalKey();

  /// Simulated login state: when set, the id is passed as `userId` so the
  /// submission can be correlated with the user — and (by default) the
  /// optional email input hides since the developer already knows who is
  /// reporting. See SimpleFeedbackConfig.emailVisibility to change that.
  String? _userId = 'u-42';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('simple_feedback example')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          if (!firebaseReady)
            const Card(
              color: Color(0xFFFFE7C4),
              child: Padding(
                padding: EdgeInsets.all(12),
                child: Text(
                  'Firebase is not configured in this example app. The dialog '
                  'opens fine, but submissions will fail until you add your '
                  'own google-services.json / GoogleService-Info.plist and run '
                  'flutterfire configure. See example/README.md.',
                ),
              ),
            ),
          const SizedBox(height: 8),
          SegmentedButton<Locale>(
            segments: const [
              ButtonSegment(value: Locale('en'), label: Text('EN')),
              ButtonSegment(value: Locale('zh'), label: Text('中文')),
              ButtonSegment(value: Locale('ja'), label: Text('日本語')),
            ],
            selected: {widget.locale},
            onSelectionChanged: (selection) =>
                widget.onLocaleChanged(selection.first),
          ),
          const SizedBox(height: 8),
          Card(
            child: Padding(
              padding: const EdgeInsets.all(12),
              child: Row(
                children: [
                  Expanded(
                    child: Text(
                      _userId == null
                          ? 'Signed out — the dialog asks for an email.'
                          : 'Signed in as $_userId — the email input hides.',
                      style: Theme.of(context).textTheme.bodySmall,
                    ),
                  ),
                  const SizedBox(width: 8),
                  TextButton(
                    onPressed: () =>
                        setState(() => _userId = _userId == null ? 'u-42' : null),
                    child: Text(_userId == null ? 'Sign in' : 'Sign out'),
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 8),
          FilledButton(
            onPressed: () => showSimpleFeedback(
              context,
              source: 'DemoPage',
              userId: _userId,
              metadata: const {'appVersion': '1.0.0'},
            ),
            child: const Text('Open feedback dialog'),
          ),
          const SizedBox(height: 24),
          // Everything below can be captured automatically as the first
          // screenshot when opening the dialog with screenshotKey.
          RepaintBoundary(
            key: _boundaryKey,
            child: Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('A card worth reporting',
                        style: Theme.of(context).textTheme.titleMedium),
                    const SizedBox(height: 8),
                    const Text(
                        'Open feedback from the button below and the card is '
                        'captured as the first screenshot automatically.'),
                    const SizedBox(height: 12),
                    FilledButton.tonal(
                      onPressed: () => showSimpleFeedback(
                        context,
                        source: 'DemoCard',
                        userId: _userId,
                        sourceData: 'card: demo\nlocale: ${widget.locale}',
                        screenshotKey: _boundaryKey,
                      ),
                      child: const Text('Feedback with page capture'),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
2
likes
160
points
291
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

An in-app feedback dialog for Flutter backed by Firebase - type selection, screenshot attachments, automatic page capture, device/user id, and built-in zh/en/ja strings.

Repository (GitHub)
View/report issues

Topics

#feedback #widget #form

License

MIT (license)

Dependencies

cloud_firestore, firebase_core, firebase_storage, flutter, image, image_picker, shared_preferences

More

Packages that depend on simple_feedback