puzzle_cube 0.2.0 copy "puzzle_cube: ^0.2.0" to clipboard
puzzle_cube: ^0.2.0 copied to clipboard

An interactive 3D twisty-puzzle cube for Flutter, with a 3x3 model (turns, scrambling, validation) and a gesture-driven widget (orbit, drag-to-turn).

example/lib/main.dart

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

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

/// Demo app showing the interactive [Cube] widget.
class PuzzleCubeExampleApp extends StatelessWidget {
  /// Creates the example app.
  const PuzzleCubeExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'puzzle_cube',
      theme: ThemeData(
        colorSchemeSeed: Colors.indigo,
        useMaterial3: true,
      ),
      home: const _HomePage(),
    );
  }
}

class _HomePage extends StatefulWidget {
  const _HomePage();

  @override
  State<_HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<_HomePage> {
  final CubeController _controller = CubeController(
    initialViewRotationX: -0.5,
    initialViewRotationY: 0.6,
  );

  CubeMove? _lastMove;

  @override
  void initState() {
    super.initState();
    _controller.addListener(_onChanged);
  }

  void _onChanged() => setState(() {});

  @override
  void dispose() {
    _controller.removeListener(_onChanged);
    _controller.dispose();
    super.dispose();
  }

  String get _statusText {
    if (_controller.isSolved) return 'Solved 🎉';
    if (_lastMove == null) return 'Drag a layer to turn it';
    return 'Last move: ${_lastMove!.name.toUpperCase()}';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('puzzle_cube'),
        actions: [
          IconButton(
            tooltip: 'Recenter',
            onPressed: _controller.resetCamera,
            icon: const Icon(Icons.center_focus_strong),
          ),
        ],
      ),
      body: SafeArea(
        child: Column(
          children: [
            const SizedBox(height: 8),
            Text(
              _statusText,
              style: Theme.of(context).textTheme.titleMedium,
            ),
            Expanded(
              child: Center(
                child: SizedBox(
                  width: 320,
                  height: 320,
                  child: Cube(
                    controller: _controller,
                    onMove: (move) {
                      _controller.applyMoveInstant(move);
                      setState(() => _lastMove = move);
                    },
                  ),
                ),
              ),
            ),
            const Divider(height: 1),
            Padding(
              padding: const EdgeInsets.all(12),
              child: Column(
                children: [
                  _MoveRow(controller: _controller),
                  const SizedBox(height: 8),
                  Row(
                    children: [
                      Expanded(
                        child: FilledButton.tonalIcon(
                          onPressed: () => _controller.scramble(),
                          icon: const Icon(Icons.shuffle),
                          label: const Text('Scramble'),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: FilledButton.tonalIcon(
                          onPressed: () {
                            _controller.reset();
                            setState(() => _lastMove = null);
                          },
                          icon: const Icon(Icons.restart_alt),
                          label: const Text('Reset'),
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  OutlinedButton.icon(
                    onPressed: () => _controller.playSequence(const [
                      CubeMove.r,
                      CubeMove.u,
                      CubeMove.ri,
                      CubeMove.ui,
                    ]),
                    icon: const Icon(Icons.play_arrow),
                    label: const Text("Play sexy move (R U R' U')"),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _MoveRow extends StatelessWidget {
  const _MoveRow({required this.controller});

  final CubeController controller;

  static const _moves = <(String, CubeMove)>[
    ('U', CubeMove.u),
    ('D', CubeMove.d),
    ('L', CubeMove.l),
    ('R', CubeMove.r),
    ('F', CubeMove.f),
    ('B', CubeMove.b),
  ];

  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 6,
      runSpacing: 6,
      alignment: WrapAlignment.center,
      children: [
        for (final (label, move) in _moves) ...[
          _key(label, move),
          _key("$label'", move.inverse),
        ],
      ],
    );
  }

  Widget _key(String label, CubeMove move) {
    return SizedBox(
      width: 44,
      child: ElevatedButton(
        onPressed: () => controller.play(move),
        style: ElevatedButton.styleFrom(
          padding: const EdgeInsets.symmetric(vertical: 10),
        ),
        child: Text(label),
      ),
    );
  }
}
8
likes
160
points
62
downloads

Documentation

API reference

Publisher

verified publisherabsolutedevs.com

Weekly Downloads

An interactive 3D twisty-puzzle cube for Flutter, with a 3x3 model (turns, scrambling, validation) and a gesture-driven widget (orbit, drag-to-turn).

Repository (GitHub)
View/report issues

Topics

#cube #puzzle #three-dim #widget

License

MIT (license)

Dependencies

ditredi, equatable, flutter, vector_math

More

Packages that depend on puzzle_cube