Flutter 3D Cube Effect

3,320

Probably you know that flutter doesn't have 3d engine. But you don't need it, you can use perspective transformations.

I've made small example for you.

enter image description here

import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter 3D Cube Effect',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      home: Scaffold(
          body: Container(
        child: SafeArea(
          top: true,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text('Flutter 3D Cube Effect'),
              Expanded(
                child: Pseudo3dSlider(),
              ),
            ],
          ),
        ),
      )),
    );
  }
}

class Pseudo3dSlider extends StatefulWidget {
  @override
  _Pseudo3dSliderState createState() => _Pseudo3dSliderState();
}

class _Pseudo3dSliderState extends State<Pseudo3dSlider> {
  Map<String, Offset> offsets = {
    'start': Offset(70, 100),
    'finish': Offset(200, 100),
    'center': Offset(100, 200),
  };

  double originX = 0;
  double x = 0;

  void onDragStart(double originX) => setState(() {
        this.originX = originX;
      });

  void onDragUpdate(double x) => setState(() {
        this.x = originX - x;
      });

  double get turnRatio {
    const step = -150.0;
    var k = x / step;
    k = k > 1 ? 1 : (k < 0 ? 0 : k);
    return 1 - k;
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onPanStart: (details) => onDragUpdate(details.globalPosition.dx),
      onPanUpdate: (details) => onDragUpdate(details.globalPosition.dx),
      child: Slider(
        children: [
          _Side(
            color: Colors.blueAccent,
            number: 1,
          ),
          _Side(
            color: Colors.redAccent.shade200,
            number: 2,
          ),
        ],
        k: turnRatio,
      ),
    );
  }
}

class _Side extends StatelessWidget {
  const _Side({Key key, this.color, this.number}) : super(key: key);

  final Color color;
  final int number;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 150,
      height: 150,
      color: color,
      child: Center(
        child: Text(
          number.toString(),
          style: TextStyle(fontSize: 14),
        ),
      ),
    );
  }
}

class Slider extends StatelessWidget {
  Slider({
    Key key,
    @required this.children,
    @required this.k,
  }) : super(key: key) {
    assert(children.length == 2, 'wronge nubmer of children');
  }

  final List<Widget> children;
  final double k;

  @override
  Widget build(BuildContext context) {
    var k1 = k;
    var k2 = 1 - k;
    print(k1);
    print(k2);
    return Row(
      children: <Widget>[
        Transform(
          transform: Matrix4.identity()
            ..setEntry(3, 2, 0.003)
            ..rotateY(pi / 2 * k1),
          alignment: FractionalOffset.centerRight,
          child: children[0],
        ),
        Transform(
          transform: Matrix4.identity()
            ..setEntry(3, 2, 0.003)
            ..rotateY(pi / 2 * -k2),
          alignment: FractionalOffset.centerLeft,
          child: children[1],
        )
      ],
    );
  }
}
Share:
3,320
Cristian F. Bustos
Author by

Cristian F. Bustos

Updated on December 16, 2022

Comments

  • Cristian F. Bustos
    Cristian F. Bustos over 1 year

    I'm asking you how I can create this effect in a Flutter?

    enter image description here

  • Cristian F. Bustos
    Cristian F. Bustos over 4 years
    Thanks for your help, but what I'm looking for is to apply the 3D/cube effect, when you transition between pages. I'm going to try what you've added in the comments. Do you know any example of transition with 3D/cube effect between pages?
  • Cristian F. Bustos
    Cristian F. Bustos over 4 years
    I have a error: The method '/' was called on null. Receiver: null Tried calling: /(-150) in this method: double get turnRatio { const step = -150.0; var k = x / step; k = k > 1 ? 1 : (k < 0 ? 0 : k); return 1 - k; }
  • Kherel
    Kherel over 4 years
    change double x; to double x = 0;
  • Kherel
    Kherel over 4 years
    Let me think about transitions between pages. It's not so hard.
  • Kherel
    Kherel over 4 years
    @CristianF.Bustos could you create new question about the route transition animation, and I will answer with my version of code.. put the link to the new question as a comment here...
  • Cristian F. Bustos
    Cristian F. Bustos over 4 years
  • emvaized
    emvaized about 4 years
    @Kherel Is is possible to recreate your animation with all four sides of the cube? Thanks.
  • Kherel
    Kherel about 4 years
    sure, for example you can have the list of sides, and change them depending on direction and position.
  • emvaized
    emvaized about 4 years
    @Kherel I tried, but found it difficult since sides are displayed in a Row. Maybe third side should be placed in a Stack under the first one?