flutter_user_recorder 0.1.0 copy "flutter_user_recorder: ^0.1.0" to clipboard
flutter_user_recorder: ^0.1.0 copied to clipboard

A powerful Flutter package for automatically recording and replaying user interactions on any screen or widget. Perfect for testing, automation, and user flow analysis.

flutter_recorder #

pub package License: MIT

A powerful Flutter package for automatically recording and replaying user interactions on any screen or widget. Perfect for testing, automation, and user flow analysis.

✨ Features #

  • 🎯 Easy Integration: Wrap your app with RecorderLayer and start recording
  • 📝 Comprehensive Recording: Records taps, text input, scrolls, navigation, drag/swipe gestures
  • 🔄 Accurate Replay: Replays interactions exactly as recorded, with timing preserved
  • 💾 Session Management: Save and manage multiple recording sessions
  • 📦 Export/Import: Export recordings as JSON for sharing or analysis
  • 🎨 Built-in Widgets: Ready-to-use widgets for common interactions
  • 🚀 Speed Control: Adjust replay speed (0.5x, 1x, 2x, 5x)
  • 🔂 Script Looping: Repeat actions seamlessly with loop functionality
  • 🎛️ Floating Control Panel: Beautiful FAB with all controls
  • 🏗️ Clean Architecture: MVVM-ready, modular, and extensible
  • 🔒 Null-Safe: Fully null-safety enabled

📦 Installation #

Add flutter_recorder to your pubspec.yaml:

dependencies:
  flutter_recorder: ^0.1.0

Then run:

flutter pub get

🚀 Quick Start #

1. Wrap your app with RecorderLayer #

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

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

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

  @override
  Widget build(BuildContext context) {
    return RecorderLayer(
      recordNavigation: true,
      recordGlobalScroll: true,
      recordDrag: true,
      child: MaterialApp(
        title: 'My App',
        home: HomePage(),
      ),
    );
  }
}

2. Use built-in recorder widgets #

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // Record text input
          RecorderTextField(
            id: 'username_field',
            decoration: InputDecoration(labelText: 'Username'),
          ),
          
          // Record button taps
          RecorderTap(
            id: 'submit_button',
            child: ElevatedButton(
              onPressed: () {
                print('Submitted!');
              },
              child: Text('Submit'),
            ),
          ),
          
          // Record scrolls
          RecorderScroll(
            id: 'my_list',
            child: ListView(
              children: [/* ... */],
            ),
          ),
        ],
      ),
      // Add floating control panel
      floatingActionButton: RecorderFAB(),
    );
  }
}

3. Control recording programmatically #

// Get the controller
final controller = RecorderLayer.of(context);

// Start recording
controller.start();

// Stop recording
controller.stop();

// Get current events
final events = controller.events;

// Save session
await controller.save();

// Load session
await controller.load();

// Export as JSON
final json = controller.export();

📖 Core Components #

RecorderLayer #

Wraps your widget tree and provides RecorderController to descendant widgets. Similar to BlocProvider.

RecorderLayer(
  controller: recorderController,  // Optional - created automatically if not provided
  replayer: replayer,              // Optional - created automatically if not provided
  recordNavigation: true,           // Record route changes
  recordGlobalScroll: true,        // Record full-screen scrolls
  recordDrag: true,                 // Record drag/swipe gestures
  minScrollDelta: 10.0,            // Minimum scroll delta to record
  child: YourApp(),
)

Important: Make sure to add the navigator observer to your MaterialApp:

MaterialApp(
  navigatorObservers: [
    RecorderLayer.navigatorObserver(RecorderLayer.of(context)),
  ],
  // ...
)

RecorderController #

Manages recording state and events. Provides methods to:

  • start() - Start recording (creates a new session)
  • stop() - Stop recording (saves the current session)
  • record() - Record an event (called automatically by widgets)
  • export() - Export current session events as JSON string
  • exportAllSessions() - Export all sessions as JSON string
  • import() - Import events from JSON string
  • importSessions() - Import sessions from JSON string
  • save() - Save all sessions to local storage
  • load() - Load all sessions from local storage
  • clear() - Clear current session events
  • deleteSession(String id) - Delete a specific session
  • clearAllSessions() - Delete all sessions
  • getSession(String id) - Get a specific session

Replayer #

Replays recorded events in order, respecting time delays.

final replayer = Replayer(
  minDelayMs: 200,        // Minimum delay between events
  respectTiming: true,    // Use original timing
  onReplayStart: () => print('Started'),
  onReplayComplete: () => print('Done'),
  onEventReplayed: (event) => print('Replayed: $event'),
  onEventFailed: (event, error) => print('Failed: $error'),
);

// Replay events
await replayer.replay(events, context: context);

// Set replay speed
replayer.setSpeed(2.0);  // 2x speed

// Set loop count
replayer.setLoopCount(5);  // Repeat 5 times

// Replay single event
await replayer.replaySingleEvent(event, context: context);

Built-in Widgets #

RecorderTap

Wraps any widget with tap gesture recording.

RecorderTap(
  id: 'my_button',
  onTap: () {
    // Your tap handler
  },
  onLongPress: () {
    // Optional long press handler
  },
  recordLongPress: true,  // Record long press events
  child: ElevatedButton(
    onPressed: () {},
    child: Text('Tap me'),
  ),
)

RecorderTextField

Wraps TextField/TextFormField with text input recording.

RecorderTextField(
  id: 'my_text_field',
  controller: textController,  // Optional
  decoration: InputDecoration(labelText: 'Enter text'),
  onChanged: (value) {
    print('Text: $value');
  },
)

RecorderScroll

Wraps scrollable widgets with scroll position recording.

RecorderScroll(
  id: 'my_list',
  minScrollDelta: 10.0,  // Minimum scroll delta to record
  recordScroll: true,     // Enable scroll recording
  child: ListView(
    children: [/* ... */],
  ),
)

Control Widgets #

RecorderFAB

A floating action button that provides a control panel for recording and replaying.

RecorderFAB(
  controller: recorderController,  // Optional - retrieved from RecorderLayer if not provided
  replayer: replayer,              // Optional - retrieved from RecorderLayer if not provided
)

Features:

  • Start/Stop recording
  • Replay current session
  • View events list
  • View sessions list
  • Speed control (0.5x, 1x, 2x, 5x)
  • Loop control (Once, 2x, 5x, 10x)

RecorderEventsList

Displays a list of recorded events for the current session.

RecorderEventsList(
  controller: recorderController,
  replayer: replayer,
)

RecorderSessionsList

Displays a list of all saved sessions.

RecorderSessionsList(
  controller: recorderController,
  replayer: replayer,
)

🎯 Advanced Usage #

Custom RecorderTarget #

Create custom recordable widgets by implementing RecorderTarget:

class MyCustomWidget extends StatefulWidget {
  final String id;
  
  @override
  State<MyCustomWidget> createState() => _MyCustomWidgetState();
}

class _MyCustomWidgetState extends State<MyCustomWidget> 
    implements RecorderTarget {
  @override
  String get id => widget.id;
  
  @override
  void initState() {
    super.initState();
    RecorderRegistry().register(this);
  }
  
  @override
  void dispose() {
    RecorderRegistry().unregister(id);
    super.dispose();
  }
  
  @override
  Future<bool> perform(RecorderEventType type, dynamic value) async {
    // Perform the action based on type and value
    if (type == RecorderEventType.tap) {
      // Handle tap
      return true;
    }
    return false;
  }
  
  void _handleInteraction() {
    final controller = RecorderLayer.maybeOf(context);
    if (controller != null && controller.isRecording) {
      final currentRoute = RecorderLayer.currentRouteOf(context);
      controller.record(
        RecorderEventType.tap,
        id,
        null,
        route: currentRoute,  // Automatically stores route with event
      );
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleInteraction,
      child: Container(/* ... */),
    );
  }
}

Event Model #

Events are represented by RecorderEvent:

class RecorderEvent {
  final RecorderEventType type;      // tap, textInput, scroll, navigation, etc.
  final String targetId;              // Widget ID
  final dynamic value;                // Event value (text, scroll position, etc.)
  final int timestamp;                // When it occurred (milliseconds since epoch)
  final Map<String, dynamic>? metadata;  // Optional metadata (route, arguments, etc.)
}

Session Model #

Sessions are represented by RecorderSession:

class RecorderSession {
  final String id;                    // Unique session ID
  final String name;                  // Session name
  final List<RecorderEvent> events;   // Events in this session
  final int startTimestamp;           // When recording started
  final int? stopTimestamp;           // When recording stopped
}

The package automatically records navigation events with arguments:

// Navigate with data
Navigator.pushNamed(
  context,
  '/second',
  arguments: {
    'name': 'John',
    'from': 'home',
  },
);

// The navigation event is automatically recorded with the arguments
// During replay, the arguments are passed to the route

Export/Import Events #

// Export current session to JSON
final jsonString = controller.export();
print(jsonString);

// Export all sessions to JSON
final allSessionsJson = controller.exportAllSessions();

// Import events from JSON
controller.import(jsonString);

// Import sessions from JSON
controller.importSessions(allSessionsJson);

// Save to device storage
await controller.save();

// Load from device storage
await controller.load();

📱 Example App #

See the example/ folder for a complete demo app that demonstrates:

  • Recording text input
  • Recording button taps
  • Recording scrolls
  • Recording navigation with data transfer
  • Replaying interactions
  • Managing multiple sessions
  • Speed control and looping
  • Using the floating control panel

Run the example:

cd example
flutter run

🏗️ Architecture #

The package follows a clean, modular architecture:

  • models.dart: Event and session data models with JSON serialization
  • registry.dart: Global registry for RecorderTarget widgets
  • recorder_controller.dart: Manages recording state, events, and sessions
  • recorder_layer.dart: InheritedWidget providing controller and replayer context
  • replayer.dart: Replays recorded events with timing, speed control, and looping
  • widgets/: Built-in recorder widgets and control panels

📋 Requirements #

  • Flutter >= 3.0.0
  • Dart >= 3.0.0

📦 Dependencies #

  • shared_preferences: ^2.2.2 - For persistent storage

🤝 Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📝 License #

This project is licensed under the MIT License - see the LICENSE file for details.

👤 Author #

Created with ❤️ for the Flutter community.

🙏 Acknowledgments #

Inspired by browser automation tools like "Action Replay" Chrome extension.

📚 Additional Resources #


Note: This package is designed for testing and automation purposes. Make sure to handle user data and privacy appropriately in your application.

2
likes
0
points
52
downloads

Publisher

unverified uploader

Weekly Downloads

A powerful Flutter package for automatically recording and replaying user interactions on any screen or widget. Perfect for testing, automation, and user flow analysis.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, shared_preferences

More

Packages that depend on flutter_user_recorder