flutter_tray 0.0.2 copy "flutter_tray: ^0.0.2" to clipboard
flutter_tray: ^0.0.2 copied to clipboard

An opinionated Flutter tray with fluid, content-sized page transitions.

example/lib/main.dart

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

void main() => runApp(const ExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Flutter Tray')),
        body: const Center(
          child: Padding(
            padding: EdgeInsets.all(24),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                _OpenStructuredTrayButton(),
                SizedBox(height: 12),
                _OpenCustomTrayButton(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _OpenStructuredTrayButton extends StatelessWidget {
  const _OpenStructuredTrayButton();

  @override
  Widget build(BuildContext context) {
    return FilledButton(
      onPressed: () => showFlutterTray<void>(
        context: context,
        presentation: FlutterTrayPresentation.floating,
        primaryActionBuilder: (trayContext) => _TrayAction(
          label: 'Next',
          onPressed: () => FlutterTray.of(trayContext).push(
            key: const ValueKey('details'),
            builder: (_) => const FlutterTrayPage(
              title: Text('Second page'),
              child: Text(
                'The content and sheet height use the same bottom-anchored '
                'transition.',
              ),
            ),
            primaryActionBuilder: (detailsContext) => _TrayAction(
              label: 'Back',
              onPressed: () => FlutterTray.of(detailsContext).pop(),
            ),
          ),
        ),
        bottomActionWrapperBuilder: (_, child) =>
            FlutterTrayActionBar(child: child),
        builder: (_) => const _ExpandableContent(),
      ),
      child: const Text('Open structured floating tray'),
    );
  }
}

class _OpenCustomTrayButton extends StatelessWidget {
  const _OpenCustomTrayButton();

  @override
  Widget build(BuildContext context) {
    return OutlinedButton(
      onPressed: () => showFlutterTray<void>(
        context: context,
        presentation: FlutterTrayPresentation.floating,
        showDragHandle: false,
        floatingMargin: const EdgeInsets.all(24),
        builder: (trayContext) => ColoredBox(
          color: Theme.of(trayContext).colorScheme.secondaryContainer,
          child: Padding(
            padding: const EdgeInsets.all(32),
            child: TextButton(
              onPressed: () => Navigator.of(trayContext).pop(),
              child: const Text('This entire child is app-owned. Close'),
            ),
          ),
        ),
      ),
      child: const Text('Open fully custom tray'),
    );
  }
}

class _ExpandableContent extends StatefulWidget {
  const _ExpandableContent();

  @override
  State<_ExpandableContent> createState() => _ExpandableContentState();
}

class _ExpandableContentState extends State<_ExpandableContent> {
  bool expanded = false;

  @override
  Widget build(BuildContext context) {
    return FlutterTrayPage(
      title: const Text('Content that moves'),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          const Text('The tray follows this content as it grows and shrinks.'),
          if (expanded) ...[
            const SizedBox(height: 16),
            const Text(
              'This extra section can contain forms, lists, media, or any '
              'other Flutter widget.',
            ),
          ],
          const SizedBox(height: 24),
          FilledButton(
            onPressed: () => setState(() => expanded = !expanded),
            child: Text(expanded ? 'Show less' : 'Show more'),
          ),
        ],
      ),
    );
  }
}

class _TrayAction extends StatelessWidget {
  const _TrayAction({
    required this.label,
    required this.onPressed,
  });

  final String label;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return Semantics(
      button: true,
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: onPressed,
        child: DecoratedBox(
          decoration: BoxDecoration(
            color: Theme.of(context).colorScheme.primary,
            borderRadius: BorderRadius.circular(18),
          ),
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              label,
              textAlign: TextAlign.center,
              style: TextStyle(
                color: Theme.of(context).colorScheme.onPrimary,
                fontWeight: FontWeight.w700,
              ),
            ),
          ),
        ),
      ),
    );
  }
}
3
likes
160
points
75
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

An opinionated Flutter tray with fluid, content-sized page transitions.

Repository (GitHub)
View/report issues

Topics

#bottom-sheet #animation #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_tray