mix 2.2.0-beta.4 copy "mix: ^2.2.0-beta.4" to clipboard
mix: ^2.2.0-beta.4 copied to clipboard

An expressive way to effortlessly build design systems in Flutter.

example/lib/main.dart

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

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

/// Runnable gallery for Mix's public WrapBox API.
class WrapBoxExampleApp extends StatelessWidget {
  const WrapBoxExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Mix WrapBox',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF5B5BD6)),
        scaffoldBackgroundColor: const Color(0xFFF5F5FA),
        useMaterial3: true,
      ),
      home: const WrapBoxExampleScreen(),
    );
  }
}

enum CloudWidth {
  narrow(220, 'Narrow'),
  wide(420, 'Wide');

  const CloudWidth(this.pixels, this.label);

  final double pixels;
  final String label;
}

/// Interactive controls plus fixed directionality examples.
class WrapBoxExampleScreen extends StatefulWidget {
  const WrapBoxExampleScreen({super.key});

  @override
  State<WrapBoxExampleScreen> createState() => _WrapBoxExampleScreenState();
}

class _WrapBoxExampleScreenState extends State<WrapBoxExampleScreen> {
  CloudWidth _width = CloudWidth.narrow;
  Axis _axis = Axis.horizontal;
  TextDirection _textDirection = TextDirection.ltr;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Mix WrapBox')),
      body: ListView(
        padding: const EdgeInsets.all(24),
        children: [
          Text(
            'Interactive tag cloud',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 8),
          const Text(
            'The constrained width makes Flutter Wrap create multiple runs.',
          ),
          const SizedBox(height: 16),
          _Controls(
            width: _width,
            axis: _axis,
            textDirection: _textDirection,
            onWidthChanged: (value) => setState(() => _width = value),
            onAxisChanged: (value) => setState(() => _axis = value),
            onTextDirectionChanged: (value) {
              setState(() => _textDirection = value);
            },
          ),
          const SizedBox(height: 20),
          Align(
            alignment: AlignmentDirectional.centerStart,
            child: WrapCloudPreview(
              width: _width.pixels,
              axis: _axis,
              textDirection: _textDirection,
              cloudKey: const Key('tag-cloud'),
            ),
          ),
          const SizedBox(height: 32),
          Text(
            'Direction matrix',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const _DirectionMatrix(),
          const SizedBox(height: 32),
          Text(
            'Advanced escape hatch',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const _AdvancedFlowExample(),
        ],
      ),
    );
  }
}

class _Controls extends StatelessWidget {
  const _Controls({
    required this.width,
    required this.axis,
    required this.textDirection,
    required this.onWidthChanged,
    required this.onAxisChanged,
    required this.onTextDirectionChanged,
  });

  final CloudWidth width;
  final Axis axis;
  final TextDirection textDirection;
  final ValueChanged<CloudWidth> onWidthChanged;
  final ValueChanged<Axis> onAxisChanged;
  final ValueChanged<TextDirection> onTextDirectionChanged;

  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 12,
      runSpacing: 12,
      children: [
        SegmentedButton<CloudWidth>(
          key: const Key('width-control'),
          segments: [
            for (final value in CloudWidth.values)
              ButtonSegment(value: value, label: Text(value.label)),
          ],
          selected: {width},
          onSelectionChanged: (values) => onWidthChanged(values.single),
        ),
        SegmentedButton<Axis>(
          key: const Key('axis-control'),
          segments: const [
            ButtonSegment(value: Axis.horizontal, label: Text('Horizontal')),
            ButtonSegment(value: Axis.vertical, label: Text('Vertical')),
          ],
          selected: {axis},
          onSelectionChanged: (values) => onAxisChanged(values.single),
        ),
        SegmentedButton<TextDirection>(
          key: const Key('direction-control'),
          segments: const [
            ButtonSegment(value: TextDirection.ltr, label: Text('LTR')),
            ButtonSegment(value: TextDirection.rtl, label: Text('RTL')),
          ],
          selected: {textDirection},
          onSelectionChanged: (values) {
            onTextDirectionChanged(values.single);
          },
        ),
      ],
    );
  }
}

/// A deterministic constrained cloud built with flattened WrapBox styling.
class WrapCloudPreview extends StatelessWidget {
  const WrapCloudPreview({
    required this.width,
    this.axis = Axis.horizontal,
    this.textDirection = TextDirection.ltr,
    this.compact = false,
    this.cloudKey,
    this.boundaryKey,
    super.key,
  });

  final double width;
  final Axis axis;
  final TextDirection textDirection;
  final bool compact;
  final Key? cloudKey;
  final Key? boundaryKey;

  static const tags = [
    'Flutter',
    'Mix',
    'Tokens',
    'Variants',
    'Themes',
    'Motion',
    'Responsive',
    'Typed',
  ];

  @override
  Widget build(BuildContext context) {
    final height = axis == Axis.horizontal ? (compact ? 142.0 : 188.0) : 150.0;
    final style = WrapBoxStyler()
        .paddingAll(compact ? 10 : 16)
        .color(const Color(0xFFFFFFFF))
        .borderRounded(18)
        .clipBehavior(Clip.antiAlias)
        .direction(axis)
        .spacing(8)
        .runSpacing(10)
        .wrapAlignment(WrapAlignment.center)
        .runAlignment(WrapAlignment.center)
        .crossAxisAlignment(WrapCrossAlignment.center)
        .textDirection(textDirection);

    return RepaintBoundary(
      key: boundaryKey,
      child: SizedBox(
        width: width,
        height: height,
        child: WrapBox(
          key: cloudKey,
          style: style,
          children: [for (final tag in tags) _Tag(label: tag)],
        ),
      ),
    );
  }
}

class _Tag extends StatelessWidget {
  const _Tag({required this.label});

  final String label;

  @override
  Widget build(BuildContext context) {
    return Box(
      style: BoxStyler()
          .paddingX(10)
          .paddingY(6)
          .color(const Color(0xFFE8E8FF))
          .borderRounded(999),
      child: Text(
        label,
        textDirection: TextDirection.ltr,
        style: const TextStyle(
          color: Color(0xFF35358A),
          fontSize: 13,
          fontWeight: FontWeight.w600,
        ),
      ),
    );
  }
}

class _DirectionMatrix extends StatelessWidget {
  const _DirectionMatrix();

  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 16,
      runSpacing: 16,
      children: const [
        _DirectionSample(
          label: 'Horizontal · LTR',
          axis: Axis.horizontal,
          textDirection: TextDirection.ltr,
        ),
        _DirectionSample(
          label: 'Horizontal · RTL',
          axis: Axis.horizontal,
          textDirection: TextDirection.rtl,
        ),
        _DirectionSample(
          label: 'Vertical · LTR',
          axis: Axis.vertical,
          textDirection: TextDirection.ltr,
        ),
        _DirectionSample(
          label: 'Vertical · RTL',
          axis: Axis.vertical,
          textDirection: TextDirection.rtl,
        ),
      ],
    );
  }
}

class _DirectionSample extends StatelessWidget {
  const _DirectionSample({
    required this.label,
    required this.axis,
    required this.textDirection,
  });

  final String label;
  final Axis axis;
  final TextDirection textDirection;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(label),
        const SizedBox(height: 6),
        WrapCloudPreview(
          width: 220,
          axis: axis,
          textDirection: textDirection,
          compact: true,
        ),
      ],
    );
  }
}

class _AdvancedFlowExample extends StatelessWidget {
  const _AdvancedFlowExample();

  @override
  Widget build(BuildContext context) {
    final style = WrapBoxStyler()
        .paddingAll(12)
        .color(const Color(0xFF202033))
        .borderRounded(14)
        .flow(
          WrapStyler(
            spacing: 8,
            runSpacing: 8,
            alignment: WrapAlignment.spaceBetween,
          ),
        );

    return ConstrainedBox(
      constraints: const BoxConstraints(maxWidth: 360),
      child: WrapBox(
        style: style,
        children: const [
          _Tag(label: 'Nested'),
          _Tag(label: 'flow'),
          _Tag(label: 'escape hatch'),
        ],
      ),
    );
  }
}
416
likes
150
points
20.1k
downloads
screenshot

Documentation

API reference

Publisher

verified publisherleoafarias.com

Weekly Downloads

An expressive way to effortlessly build design systems in Flutter.

License

BSD-3-Clause (license)

Dependencies

flutter, mix_annotations

More

Packages that depend on mix