flog_flow 0.4.5 copy "flog_flow: ^0.4.5" to clipboard
flog_flow: ^0.4.5 copied to clipboard

Graba el flujo de navegación real del usuario (pantallas, taps, red, logs y errores) y lo exporta en un archivo cifrado .flowx que se reproduce en un visor web.

example/lib/main.dart

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

void main() {
  runApp(const DemoApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlogFlow demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
          useMaterial3: true, colorSchemeSeed: const Color(0xFF5B54E6)),
      // 1) Observa la navegación.
      navigatorObservers: [FlogFlow.instance.navigatorObserver],
      // 2) Envuelve toda la app para capturar taps + screenshots.
      builder: (context, child) => FlogFlogScope(child: child!),
      initialRoute: '/',
      routes: {
        '/': (_) => const StartPage(),
        '/password': (_) => const PasswordPage(),
        '/dashboard': (_) => const DashboardPage(),
        '/details': (_) => const DetailsPage(),
        '/signup': (_) => const SignUpPage(),
      },
    );
  }
}

class StartPage extends StatefulWidget {
  const StartPage({super.key});
  @override
  State<StartPage> createState() => _StartPageState();
}

class _StartPageState extends State<StartPage> {
  @override
  void initState() {
    super.initState();
    // 3) Empieza a grabar al arrancar.
    WidgetsBinding.instance.addPostFrameCallback((_) {
      FlogFlow.instance.start(
        appName: 'Demo App',
        initialRouteName: '/',
        // El export siempre va cifrado: define una clave.
        config: const CaptureConfig(encryptionPassphrase: 'demo-clave'),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const FlutterLogo(size: 96),
            const SizedBox(height: 24),
            const Text('Start page', style: TextStyle(fontSize: 22)),
            const SizedBox(height: 24),
            FilledButton(
              onPressed: () {
                // 4) Etiqueta la acción ANTES de navegar.
                FlogFlow.instance.tag('User clicks "Sign in"');
                Navigator.pushNamed(context, '/password');
              },
              child: const Text('Sign in'),
            ),
            const SizedBox(height: 12),
            TextButton(
              onPressed: () {
                FlogFlow.instance.tag('User clicks "Sign up"');
                Navigator.pushNamed(context, '/signup');
              },
              child: const Text('Sign up'),
            ),
          ],
        ),
      ),
    );
  }
}

class PasswordPage extends StatelessWidget {
  const PasswordPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Password')),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            const TextField(decoration: InputDecoration(labelText: 'Email')),
            const TextField(
                obscureText: true,
                decoration: InputDecoration(labelText: 'Password')),
            const SizedBox(height: 24),
            FilledButton(
              onPressed: () {
                FlogFlow.instance.tag('Submit credentials');
                Navigator.pushNamed(context, '/dashboard');
              },
              child: const Text('Continue'),
            ),
          ],
        ),
      ),
    );
  }
}

class DashboardPage extends StatelessWidget {
  const DashboardPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Dashboard')),
      body: ListView(
        children: [
          SizedBox(
            height: 90,
            child: ListView(
              scrollDirection: Axis.horizontal,
              padding: const EdgeInsets.all(12),
              children: List.generate(
                8,
                (i) => const Padding(
                  padding: EdgeInsets.all(8),
                  child: CircleAvatar(radius: 28, child: Icon(Icons.person)),
                ),
              ),
            ),
          ),
          for (var i = 0; i < 6; i++)
            Card(
              margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              child: ListTile(
                leading: const Icon(Icons.image),
                title: Text('Card #$i'),
                onTap: () {
                  FlogFlow.instance.tag('Open details #$i');
                  Navigator.pushNamed(context, '/details');
                },
              ),
            ),
        ],
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  const DetailsPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Details')),
      floatingActionButton: FloatingActionButton.extended(
        icon: const Icon(Icons.download),
        label: const Text('Export'),
        onPressed: () async {
          await FlogFlow.instance.stop();
          final file = await FlogFlow.instance.export();
          if (context.mounted) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Exportado: ${file.path}')),
            );
          }
        },
      ),
      body: const Center(
        child: Icon(Icons.donut_large, size: 160, color: Color(0xFF5B54E6)),
      ),
    );
  }
}

class SignUpPage extends StatelessWidget {
  const SignUpPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sign up form')),
      body: ListView(
        padding: const EdgeInsets.all(24),
        children: [
          for (final f in ['Name', 'Email', 'Password', 'Confirm'])
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 8),
              child: TextField(decoration: InputDecoration(labelText: f)),
            ),
          const SizedBox(height: 16),
          FilledButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Create account'),
          ),
        ],
      ),
    );
  }
}
9
likes
0
points
713
downloads

Publisher

verified publisheralecodeando.dev

Weekly Downloads

Graba el flujo de navegación real del usuario (pantallas, taps, red, logs y errores) y lo exporta en un archivo cifrado .flowx que se reproduce en un visor web.

Homepage

Topics

#debugging #session-replay #monitoring #analytics

License

unknown (license)

Dependencies

archive, cryptography, dio, flutter, image, path, path_provider

More

Packages that depend on flog_flow