rive 0.14.0-dev.7 copy "rive: ^0.14.0-dev.7" to clipboard
rive: ^0.14.0-dev.7 copied to clipboard

Rive Flutter Runtime. This package provides runtime functionality for Rive graphics built with the Rive editor available at https://rive.app

example/lib/main.dart

// ignore_for_file: deprecated_member_use

import 'package:flutter/material.dart';
import 'package:rive_example/advanced/advanced.dart';
import 'package:rive_example/colors.dart';
import 'package:rive_example/examples/examples.dart';
import 'package:rive/rive.dart' as rive;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await rive.RiveNative.init();

  runApp(
    MaterialApp(
      title: 'Rive Example',
      home: const RiveExampleApp(),
      darkTheme: ThemeData(
        fontFamily: 'JetBrainsMono',
        brightness: Brightness.dark,
        scaffoldBackgroundColor: backgroundColor,
        appBarTheme: const AppBarTheme(backgroundColor: appBarColor),
        colorScheme: ColorScheme.fromSwatch(brightness: Brightness.dark)
            .copyWith(primary: primaryColor),
      ),
      themeMode: ThemeMode.dark,
    ),
  );
}

/// Determines which factory/renderer to use for the Rive examples.
///
/// In your app you can combine the usage of the Rive Renderer and the Flutter
/// Renderer. For this example app we have a static variable to determine which
/// factory to use app wide.
///
/// - `rive` uses the Rive Renderer
/// - `flutter` uses the Flutter Renderer (Skia / Impeller)
enum RiveFactoryToUse {
  rive,
  flutter,
}

/// An example application demoing Rive.
class RiveExampleApp extends StatefulWidget {
  const RiveExampleApp({Key? key}) : super(key: key);

  static RiveFactoryToUse factoryToUse = RiveFactoryToUse.rive;

  static rive.Factory get getCurrentFactory => switch (factoryToUse) {
        RiveFactoryToUse.rive => rive.Factory.rive,
        RiveFactoryToUse.flutter => rive.Factory.flutter,
      };

  @override
  State<RiveExampleApp> createState() => _RiveExampleAppState();
}

class _RiveExampleAppState extends State<RiveExampleApp> {
  // ScrollController for the CustomScrollView
  final ScrollController _scrollController = ScrollController();

  // Examples organized into sections
  final _sections = [
    const _Section(
      'Getting Started',
      [
        _Page('Rive Widget', ExampleRiveWidget(),
            'Simple example usage of the Rive widget with common parameters.'),
        _Page('Rive Widget Builder', ExampleRiveWidgetBuilder(),
            'Example usage of the Rive builder widget with common parameters.'),
        _Page('Rive Panel [Shared Texture]', ExampleRivePanel(),
            'Example usage of the Shared Texture View widget.'),
      ],
    ),
    const _Section(
      'Rive Features',
      [
        _Page('Data Binding', ExampleDataBinding(),
            'Example using Rive data binding at runtime.'),
        _Page('Data Binding - Images', ExampleDataBindingImages(),
            'Example using Rive data binding images at runtime.'),
        _Page('Data Binding - Artboards', ExampleDataBindingArtboards(),
            'Example using Rive data binding artboards at runtime.'),
        _Page('Data Binding - Lists', ExampleDataBindingLists(),
            'Example using Rive data binding lists at runtime.'),
        _Page('Responsive Layouts', ExampleResponsiveLayouts(),
            'Create responsive Rive graphics that adapt to screen size.'),
        _Page('Events', ExampleEvents(), 'Handle Rive events.'),
        _Page('Audio', ExampleRiveAudio(), 'Example Rive file with audio.'),
      ],
    ),
    const _Section(
      'Asset Loading',
      [
        _Page('Network .riv Asset', ExampleNetworkAsset(),
            'Load and display Rive graphics from network URLs.'),
        _Page('Out-of-band Assets', ExampleOutOfBandAssetLoading(),
            'Load Rive files with external assets (images, audio) separately.'),
        _Page(
          'Out-of-band Assets - Cached',
          ExampleOutOfBandCachedAssetLoading(),
          'Load Rive files with cached external assets for better immediate availability.',
        ),
      ],
    ),
    const _Section(
      'Painters [Advanced]',
      [
        _Page('State Machine Painter', ExampleStateMachinePainter(),
            'Advanced: Custom painter for state machines.'),
        _Page('Single Animation Painter', ExampleSingleAnimationPainter(),
            'Advanced: Custom painter for single animation playback.'),
        _Page('Centaur Game', CentaurGameWidget(), 'Advanced: Centaur Game.'),
      ],
    ),
    const _Section(
      'Flutter Concepts/Integration',
      [
        // _Page('Flutter Lists', Todo(),
        //     'Integrate Rive graphics with Flutter list widgets.'),
        _Page(
            'Pause/Play', ExamplePausePlay(), 'Pause and play Rive graphics.'),
        _Page('Flutter Hit Test + Cursor Behaviour', ExampleHitTestBehaviour(),
            'Specifying hit test and cursor behaviour.'),
        _Page('Flutter Ticker Mode', ExampleTickerMode(),
            'Rive graphics respect Flutter ticker mode.'),
        _Page('Flutter Time Dilation', ExampleTimeDilation(),
            'Rive graphics respect Flutter time dilation.'),
        // _Page('Flutter Hero Transitions', Todo(),
        //     'Create smooth transitions between pages with Rive graphics.'),
        // _Page('Flutter State Management', Todo(),
        //     'Manage Rive state with Flutter state management.'),
        // _Page('Flutter Localization', Todo(),
        //     'Localize Rive graphics for different languages.'),
        // _Page('Flutter Internationalization', Todo(),
        //     'Internationalize Rive graphics with Flutter i18n.'),
      ],
    ),
    const _Section(
      'Legacy Features [Use data binding instead]',
      [
        _Page('Inputs [Nested]', ExampleInputs(),
            'Legacy: Handle input [nested] controls in Rive graphics.'),
        _Page('Text Runs [Nested]', ExampleTextRuns(),
            'Legacy: Handle text runs [nested] components in Rive graphics.'),
      ],
    ),
  ];

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Rive Examples')),
      body: Column(children: [
        Expanded(
          child: Scrollbar(
            controller: _scrollController,
            child: CustomScrollView(
              controller: _scrollController,
              slivers: [
                SliverPadding(
                  padding: const EdgeInsets.all(8.0),
                  sliver: SliverList(
                    delegate: SliverChildBuilderDelegate(
                      (context, index) {
                        // Calculate which section and item we're at
                        int itemIndex = index;

                        for (int i = 0; i < _sections.length; i++) {
                          if (itemIndex == 0) {
                            // This is a section header
                            return _SectionHeader(_sections[i].title);
                          }
                          itemIndex--;

                          if (itemIndex < _sections[i].pages.length) {
                            // This is a page within the current section
                            return Padding(
                              padding: const EdgeInsets.only(bottom: 16.0),
                              child: _NavButton(
                                  page: _sections[i].pages[itemIndex]),
                            );
                          }
                          itemIndex -= _sections[i].pages.length;
                        }

                        return null;
                      },
                      childCount: _getTotalItemCount(),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
        const SizedBox(height: 16),
        ColoredBox(
          color: Colors.black,
          child: Column(
            children: [
              const SizedBox(height: 16),
              const Text('Factory to use:',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
              const SizedBox(height: 16),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Wrap(
                  alignment: WrapAlignment.center,
                  spacing: 16,
                  runSpacing: 8,
                  children: [
                    Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Radio<RiveFactoryToUse>(
                          value: RiveFactoryToUse.rive,
                          groupValue: RiveExampleApp.factoryToUse,
                          onChanged: (value) {
                            setState(() {
                              RiveExampleApp.factoryToUse =
                                  value as RiveFactoryToUse;
                            });
                          },
                        ),
                        const Text('Rive Renderer',
                            style: TextStyle(fontSize: 14)),
                      ],
                    ),
                    Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Radio<RiveFactoryToUse>(
                          value: RiveFactoryToUse.flutter,
                          groupValue: RiveExampleApp.factoryToUse,
                          onChanged: (value) {
                            setState(() {
                              RiveExampleApp.factoryToUse =
                                  value as RiveFactoryToUse;
                            });
                          },
                        ),
                        const Text('Flutter Renderer',
                            style: TextStyle(fontSize: 14)),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ]),
    );
  }

  int _getTotalItemCount() {
    int count = 0;
    for (final section in _sections) {
      count += 1; // Section header
      count += section.pages.length; // Pages in section
    }
    return count;
  }
}

/// Class used to organize demo sections.
class _Section {
  final String title;
  final List<_Page> pages;

  const _Section(this.title, this.pages);
}

/// Class used to organize demo pages.
class _Page {
  final String name;
  final Widget page;
  final String description;

  const _Page(this.name, this.page, this.description);
}

/// Section header widget with divider.
class _SectionHeader extends StatelessWidget {
  const _SectionHeader(this.title);

  final String title;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const SizedBox(height: 16),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
          child: Text(
            title,
            style: Theme.of(context).textTheme.labelLarge?.copyWith(
                  color: primaryColor,
                ),
          ),
        ),
        const Divider(
          color: primaryColor,
          thickness: 0.5,
          height: 1,
        ),
        const SizedBox(height: 16),
      ],
    );
  }
}

/// Button to navigate to demo pages with hover overlay.
class _NavButton extends StatefulWidget {
  const _NavButton({required this.page});

  final _Page page;

  @override
  State<_NavButton> createState() => _NavButtonState();
}

class _NavButtonState extends State<_NavButton> {
  bool _isHovered = false;
  OverlayEntry? _overlayEntry;

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

  void _removeOverlay() {
    _overlayEntry?.remove();
    _overlayEntry = null;
  }

  void _showOverlay() {
    _removeOverlay();

    final RenderBox renderBox = context.findRenderObject() as RenderBox;
    final position = renderBox.localToGlobal(Offset.zero);
    final size = renderBox.size;

    _overlayEntry = OverlayEntry(
      builder: (context) => Positioned(
        top: position.dy - 80, // Position above the button
        left: position.dx + (size.width / 2) - 150, // Center horizontally
        child: Material(
          color: Colors.transparent,
          child: Container(
            width: 300,
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: Colors.black.withOpacity(0.9),
              borderRadius: BorderRadius.circular(8),
              border: Border.all(color: primaryColor, width: 1),
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withOpacity(0.3),
                  blurRadius: 8,
                  offset: const Offset(0, 4),
                ),
              ],
            ),
            child: Text(
              widget.page.description,
              style: const TextStyle(
                color: Colors.white,
                fontSize: 12,
                height: 1.4,
              ),
              textAlign: TextAlign.center,
            ),
          ),
        ),
      ),
    );

    Overlay.of(context).insert(_overlayEntry!);
  }

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) {
        setState(() => _isHovered = true);
        _showOverlay();
      },
      onExit: (_) {
        setState(() => _isHovered = false);
        _removeOverlay();
      },
      child: Center(
        child: ElevatedButton(
          style: ElevatedButton.styleFrom(
            backgroundColor: _isHovered ? primaryColor.withOpacity(0.1) : null,
            elevation: _isHovered ? 8 : 2,
          ),
          child: SizedBox(
            width: 300,
            child: Center(
              child: Text(
                widget.page.name,
                style: Theme.of(context).textTheme.labelLarge,
              ),
            ),
          ),
          onPressed: () {
            _removeOverlay();
            Navigator.push(
              context,
              MaterialPageRoute<void>(
                builder: (context) => _WrappedPage(page: widget.page),
              ),
            );
          },
        ),
      ),
    );
  }
}

/// Scaffold wrapper for the page.
class _WrappedPage extends StatelessWidget {
  const _WrappedPage({required this.page});

  final _Page page;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(page.name)),
      body: page.page,
    );
  }
}
1.88k
likes
0
points
416k
downloads

Publisher

verified publisherrive.app

Weekly Downloads

Rive Flutter Runtime. This package provides runtime functionality for Rive graphics built with the Rive editor available at https://rive.app

Homepage
Repository (GitHub)
View/report issues

Topics

#animation #ui #effects #design #games

License

unknown (license)

Dependencies

flutter, flutter_web_plugins, meta, rive_native

More

Packages that depend on rive