Flutter 3D立方体效果

Flutter 3D Cube Effect

我问你如何在 Flutter 中创建这种效果?

可能你知道 flutter 没有 3d 引擎。但是你不需要它,你可以使用透视变换。

我给你做了个小例子

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],
        )
      ],
    );
  }
}