flutter_stage 0.0.1

  • Readme
  • Changelog
  • Example
  • Installing
  • 58

Flutter Stage #

pub package

A widget that positions its children in a 3D scene.

Getting Started #

Add flutter_stage as a dependency in your pubspec.yaml file.

dependencies:
  flutter_stage: ^0.0.1

Import package.

import 'package:flutter_stage/flutter_stage.dart';
... ...
  
class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Stage(
        onSceneCreated: (Scene scene) {
          scene.camera.position.setFrom(Vector3(0, 0, 1000));
          scene.camera.updateTransform();
        },
        children: [
          Actor(
            position: Vector3(0, 0, 0),
            rotation: Vector3(0, 30, 0),
            children: [
              Actor(position: Vector3(0, 0, 300), rotation: Vector3(0, 0, 0), width: 600, height: 600, widget: Container(color: Colors.red.withOpacity(0.5))),
              Actor(position: Vector3(300, 0, 0), rotation: Vector3(0, 90, 0), width: 600, height: 600, widget: Container(color: Colors.green.withOpacity(0.5))),
              Actor(position: Vector3(0, 0, -300), rotation: Vector3(0, 180, 0), width: 600, height: 600, widget: Container(color: Colors.blue.withOpacity(0.5))),
              Actor(position: Vector3(-300, 0, 0), rotation: Vector3(0, 270, 0), width: 600, height: 600, widget: Container(color: Colors.yellow.withOpacity(0.5))),
              Actor(position: Vector3(0, -300, 0), rotation: Vector3(90, 0, 0), width: 600, height: 600, widget: Container(color: Colors.pink.withOpacity(0.5))),
              Actor(position: Vector3(0, 300, 0), rotation: Vector3(270, 0, 0), width: 600, height: 600, widget: Container(color: Colors.white.withOpacity(0.5)))
            ],
          ),
        ],
      ),
    );
  }

Screenshot #

screenshot

Flutter Clock Challenge submission

[0.0.1] - TODO: Add release date. #

  • TODO: Describe initial release.

example/lib/main.dart

import 'package:flare_flutter/flare_actor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_stage/flutter_stage.dart';
import 'package:intl/intl.dart';
import 'control.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Stage',
      theme: ThemeData.dark(),
      home: MyHomePage(title: 'Flutter Stage Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

/// Weather condition in English.
enum WeatherCondition {
  cloudy,
  foggy,
  rainy,
  snowy,
  sunny,
  thunderstorm,
  windy,
}

/// Temperature unit of measurement.
enum TemperatureUnit {
  celsius,
  fahrenheit,
}

class _MyHomePageState extends State<MyHomePage> {
  Scene scene;
  String _location = 'Mountain View, CA';
  num _temperature = 22.0;
  WeatherCondition _weatherCondition = WeatherCondition.sunny;
  TemperatureUnit _unit = TemperatureUnit.celsius;

  ClockTimeControl _clockControl = ClockTimeControl();
  ClockHandControl _clockHandControl = ClockHandControl();

  /// Temperature unit of measurement with degrees.
  String get unitString {
    switch (_unit) {
      case TemperatureUnit.fahrenheit:
        return '°F';
      case TemperatureUnit.celsius:
      default:
        return '°C';
    }
  }

  num _convertFromCelsius(num degreesCelsius) {
    switch (_unit) {
      case TemperatureUnit.fahrenheit:
        return 32.0 + degreesCelsius * 9.0 / 5.0;
      case TemperatureUnit.celsius:
      default:
        return degreesCelsius;
        break;
    }
  }

  num _convertToCelsius(num degrees) {
    switch (_unit) {
      case TemperatureUnit.fahrenheit:
        return (degrees - 32.0) * 5.0 / 9.0;
      case TemperatureUnit.celsius:
      default:
        return degrees;
        break;
    }
  }

  /// Removes the enum type and returns the value as a String.
  String enumToString(Object e) => e.toString().split('.').last;

  Widget _enumMenu<T>(String label, T value, List<T> items, ValueChanged<T> onChanged) {
    return InputDecorator(
      decoration: InputDecoration(
        labelText: label,
      ),
      child: DropdownButtonHideUnderline(
        child: DropdownButton<T>(
          value: value,
          isDense: true,
          onChanged: onChanged,
          items: items.map((T item) {
            return DropdownMenuItem<T>(
              value: item,
              child: Text(enumToString(item)),
            );
          }).toList(),
        ),
      ),
    );
  }

  Widget _textField(String currentValue, String label, ValueChanged<Null> onChanged) {
    return TextField(
      decoration: InputDecoration(
        hintText: currentValue,
        helperText: label,
      ),
      onChanged: onChanged,
    );
  }

  void _onSceneCreated(Scene scene) {
    this.scene = scene;
    scene.camera.position.setFrom(Vector3(0, 0, 1000));
    scene.camera.updateTransform();
  }

  Actor makeBlock({String name, Vector3 position, double size, List<Actor> faces}) {
    final double radius = size / 2 - size * 0.0015;
    return Actor(name: name, position: position, children: [
      Actor(name: faces[0].name, position: Vector3(0, 0, radius), rotation: Vector3(0, 0, 0), width: size, height: size, widget: faces[0].widget, children: faces[0].children),
      Actor(name: faces[1].name, position: Vector3(radius, 0, 0), rotation: Vector3(0, 90, 0), width: size, height: size, widget: faces[1].widget, children: faces[1].children),
      Actor(name: faces[2].name, position: Vector3(0, 0, -radius), rotation: Vector3(0, 180, 0), width: size, height: size, widget: faces[2].widget, children: faces[2].children),
      Actor(name: faces[3].name, position: Vector3(-radius, 0, 0), rotation: Vector3(0, 270, 0), width: size, height: size, widget: faces[3].widget, children: faces[3].children),
      Actor(name: faces[4].name, position: Vector3(0, -radius, 0), rotation: Vector3(90, 0, 0), width: size, height: size, widget: faces[4].widget, children: faces[4].children),
      Actor(name: faces[5].name, position: Vector3(0, radius, 0), rotation: Vector3(270, 0, 0), width: size, height: size, widget: faces[5].widget, children: faces[5].children),
    ]);
  }

  Widget makeBlockFace(double size) {
    return Container(
      width: size,
      height: size,
      decoration: BoxDecoration(
        border: Border.all(color: Color.fromRGBO(77, 77, 77, 1.0), width: size * 0.005),
        gradient: LinearGradient(
          colors: [Color.fromRGBO(25, 25, 25, 1.0), Color.fromRGBO(56, 56, 56, 1.0), Color.fromRGBO(25, 25, 25, 1.0)],
          stops: [0.1, 0.5, 0.9],
          begin: FractionalOffset.topRight,
          end: FractionalOffset.bottomLeft,
          tileMode: TileMode.repeated,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final double blockSize = 600;
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Stage(
        onSceneCreated: _onSceneCreated,
        children: [
          makeBlock(
            name: 'block',
            position: Vector3(0, 0, 0),
            size: blockSize * 1.1,
            faces: [
              Actor(
                name: 'front',
                widget: makeBlockFace(blockSize),
                children: [
                  Actor(
                    position: Vector3(0, 0, blockSize * 0.06),
                    width: blockSize,
                    height: blockSize,
                    widget: Container(key: ValueKey('hour'), child: FlareActor('assets/clock/hour.flr', animation: 'idle', controller: _clockControl)),
                  ),
                  Actor(
                    position: Vector3(0, 0, 0),
                    width: blockSize,
                    height: blockSize,
                    widget: Container(key: ValueKey('minute'), child: FlareActor('assets/clock/minute.flr')),
                  ),
                  Actor(
                    position: Vector3(0, 0, blockSize * 0.068),
                    width: blockSize,
                    height: blockSize,
                    widget: Container(key: ValueKey('hour_hand'), child: FlareActor('assets/clock/hand.flr', controller: _clockHandControl)),
                  ),
                ],
              ),
              Actor(
                name: 'right',
                widget: makeBlockFace(blockSize),
                children: [
                  Actor(
                    position: Vector3(0, 0, 0),
                    width: blockSize,
                    height: blockSize * 0.85,
                    widget: Stack(
                      children: <Widget>[
                        Align(
                          alignment: Alignment.topCenter,
                          child: Text(
                            DateFormat('MMMM').format(DateTime.now()),
                            style: TextStyle(fontSize: blockSize * 0.15, color: Colors.white.withOpacity(0.5)),
                          ),
                        ),
                        Align(
                          alignment: Alignment.bottomCenter,
                          child: Text(
                            DateFormat('EEEE').format(DateTime.now()),
                            style: TextStyle(fontSize: blockSize * 0.13, color: Colors.white.withOpacity(0.5)),
                          ),
                        )
                      ],
                    ),
                  ),
                  Actor(
                    position: Vector3(0, 0, blockSize * 0.02),
                    width: blockSize,
                    height: blockSize,
                    widget: Center(
                      child: Text(
                        DateFormat('d').format(DateTime.now()),
                        style: TextStyle(fontSize: blockSize * 0.5, fontWeight: FontWeight.bold, color: Colors.white.withOpacity(0.6)),
                      ),
                    ),
                  ),
                  Actor(
                    position: Vector3(0, 0, 0),
                    width: blockSize,
                    height: blockSize,
                    widget: Center(
                      child: Text(
                        DateFormat('d').format(DateTime.now()),
                        style: TextStyle(fontSize: blockSize * 0.5, fontWeight: FontWeight.bold, color: Colors.black.withOpacity(0.3)),
                      ),
                    ),
                  ),
                ],
              ),
              Actor(
                name: 'back',
                widget: makeBlockFace(blockSize),
                children: [
                  Actor(
                    position: Vector3(0, 0, 0),
                    width: blockSize * 0.8,
                    height: blockSize * 0.8,
                    widget: Column(
                      children: <Widget>[
                        _textField(_location, 'Location', (String location) {
                          setState(() {
                            _location = location;
                          });
                        }),
                        _textField(_convertFromCelsius(_temperature).toStringAsFixed(0), 'Temperature', (String temperature) {
                          setState(() {
                            _temperature = _convertToCelsius(double.parse(temperature));
                          });
                        }),
                        _enumMenu('Weather', _weatherCondition, WeatherCondition.values, (WeatherCondition condition) {
                          setState(() {
                            _weatherCondition = condition;
                          });
                        }),
                        _enumMenu('Units', _unit, TemperatureUnit.values, (TemperatureUnit unit) {
                          setState(() {
                            _unit = unit;
                          });
                        }),
                      ],
                    ),
                  ),
                ],
              ),
              Actor(
                name: 'left',
                widget: makeBlockFace(blockSize),
                children: [
                  Actor(
                    position: Vector3(0, 0, 0),
                    width: blockSize,
                    height: blockSize * 0.85,
                    widget: Stack(
                      children: <Widget>[
                        Align(
                          alignment: Alignment.topCenter,
                          child: FittedBox(child: Text(_location, style: TextStyle(fontSize: blockSize * 0.09, color: Colors.white.withOpacity(0.35)))),
                        ),
                        Align(
                          alignment: Alignment.bottomCenter,
                          child: Text(enumToString(_weatherCondition), style: TextStyle(fontSize: blockSize * 0.13, color: Colors.white.withOpacity(0.6))),
                        ),
                      ],
                    ),
                  ),
                  Actor(
                    position: Vector3(0, 0, blockSize * 0.02),
                    width: blockSize,
                    height: blockSize,
                    widget: Center(
                      child: Text(
                        _convertFromCelsius(_temperature).toStringAsFixed(0) + unitString,
                        style: TextStyle(fontSize: blockSize / 3, color: Colors.white.withOpacity(0.6)),
                      ),
                    ),
                  ),
                  Actor(
                    position: Vector3(0, 0, 0),
                    width: blockSize,
                    height: blockSize,
                    widget: Center(
                      child: Text(
                        _convertFromCelsius(_temperature).toStringAsFixed(0) + unitString,
                        style: TextStyle(fontSize: blockSize / 3, color: Colors.black.withOpacity(0.3)),
                      ),
                    ),
                  ),
                ],
              ),
              Actor(name: 'top', widget: makeBlockFace(blockSize)),
              Actor(name: 'bottom', widget: makeBlockFace(blockSize)),
            ],
          ),
        ],
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_stage: ^0.0.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_stage/flutter_stage.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
32
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
61
Overall:
Weighted score of the above. [more]
58
Learn more about scoring.

We analyzed this package on Mar 27, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Health suggestions

Format lib/src/stage.dart.

Run flutter format to format lib/src/stage.dart.

Maintenance issues and suggestions

Use constrained dependencies. (-20 points)

The pubspec.yaml contains 1 dependency without version constraints. Specify version ranges for the following dependencies: vector_math.

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

The package description is too short. (-9 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
vector_math any 2.0.8
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
Dev dependencies
flutter_test