create_atom 2.0.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 62

create_atom | flutter flutter flutter #

An animated widget that displays atom with a three electrons revolving around a nucleus at its center.

2.0x 1.0x 0.5x
size: 200 size: 100 size: 50

Some Famous logos using Atom #

Name Code Logo

React Logo

        Atom(
          size: 200.0,
          orbit1Angle: math.pi / 2,
          orbit2Angle: math.pi / 6,
          orbit3Angle: - math.pi / 6,
          orbitsColor: Color(0xFF00D8FF),
          electronsColor: Color(0xFF00D8FF),
          nucleusColor: Color(0xFF00D8FF),
        ),
        
react-logo

Atom Text Editor Logo

        Container(
          width: 300.0,
          height: 300.0,
          color: Color(0xFF323232),
          child: Atom(
            size: 200.0,
            orbit1Angle: math.pi / 1.9,
            orbit2Angle: math.pi / 5,
            orbit3Angle: -math.pi / 7,
            orbitsColor: Color(0xFFEED9B7),
            electronsColor: Color(0xFFEED9B7),
            nucleusColor: Color(0xFFEED9B7),
          ),
        ),
        
atom-editor-logo

Features #

  • Can be scaled upto any size within parent widget boundaries
  • Smooth animations with mathematically pre-derived ellipse path equation
  • Transparent background for flexible usage with colors
  • 3 electron orbits (support for more may come later)
  • Orbits angle can be changed
  • Orbits color can be changed (support for individual color may come later)
  • Nucleus color can be changed
  • Electrons color can be changed (support for individual color may come later)
  • Individual electron's revolution duration can be changed
  • Any widget can be added in the center instead of nucleus

Getting Started #

In the pubspec.yaml of your flutter project, add the following dependency:

dependencies:
  ...
  create_atom: ^2.0.0

In your library add the following import:

import 'package:create_atom/create_atom.dart';

After that run flutter pub get

For help getting started with Flutter, view the online documentation.

Usage #

...
child: Atom(
  size: 100.0,
),
...

Yeah, just like that. All properties are optional except size property.

You will get a black atom by default.

centerWidget property usage -

Assign centerWidget with any widget to display that Widget instead of nucleus.

...
child: Atom(
  size: 100.0,
  centerWidget: Text("At Center"),
),
...

Note: If both nucleusColor and centerWidget are set then the Widget will get preference.

Properties #

Definitions -

PropertyDefinition
sizeDefines size of the atom's container
orbit1angleDefines 1st orbit's angle in radians
orbit2angleDefines 2nd orbit's angle in radians
orbit3angleDefines 3rd orbit's angle in radians
nucleusColorDefines Nucleus Color
orbitsColorDefines Orbits Color
electronsColorDefines Electrons Color
animDuration1Defines Animation Duration of 1st electron
animDuration2Defines Animation Duration of 2nd electron
animDuration3Defines Animation Duration of 3rd electron
centerWidgetDefines a widget to display at center

Types and Initial Values -

PropertyTypeInitial Value
sizedoublenull (required)
orbit1angledouble0.0 (Radians), 0.0 (Degrees)
orbit2angledoublepi/3 (Radians), 60.0 (Degrees)
orbit3angledouble-pi/3 (Radians), -60.0 (Degrees)
nucleusColorColorColors.black
orbitsColorColorColors.black
electronsColorColorColors.black
animDuration1DurationDuration(seconds: 1)
animDuration2DurationDuration(seconds: 2)
animDuration3DurationDuration(seconds: 3)
centerWidgetWidgetnull

Example App #

Checkout here - https://abhi011999.github.io/create_atom_flutter/ #

Random Atoms Atoms Playground

Code

Changelog #

Breaking Changes from v1.4.0 kindly see changelog #

See CHANGELOG.md for recent changes.

Contributions #

You can be of great help if you want to improve or add anything !

  • If you found a bug, open an issue.
  • If you have a feature request, open an issue.
  • If you want to contribute, submit a pull request.

License #

Package and all code within licensed under MIT License.

[2.0.0] - 2019-06-13 #

  • package -

    • Now the Atom's stack uses flutter's painter library implementation to draw the atom and its animations.
    • Painter library uses Skia graphics renderer to render graphical entities, so performance boosted by a factor 10 !
    • Some size and scaling factors slightly reduced.
    • Reduced many widgets in the Atom's build implementation to make it more performant.
    • Heavy code-refactor and reduced LOC.
    • Implemented dual EaseInOut curve in a single curved path using CatmullRomCurve.
    • Tried my best to mimic the original EaseInOut curve for both halves of the electron path.
    • You can find more details here - https://api.flutter.dev/flutter/animation/CatmullRomCurve-class.html.
  • example app -

[1.4.0] - 2019-04-25 (BREAKING CHANGE) #

  • package -

    • Replaced scale property with size property by implementing new logic of creating atom stack.

      For users below v1.4.0 - Replace scale property with size in your corresponding code and set the value of size in accordance with this example -

      • If suppose scale is 1.0 then size will be 215.0 * 1.0 = 215
      • If suppose scale is 0.3 then size will be 215.0 * 0.3 = 64.5 Because 215.0 was hard-coded value by default.
    • Fixed centerWidget scaling issue, now it scales with the atom

    • Added extensive code documentation

    • Re-factored and re-structured the libraries

  • example app -

    • Updated app with the breaking change

[1.3.0] - 2019-04-21 #

  • package -

    • Added a new centerWidget property and feature
    • Corrected orbit angles in degrees
    • Fixed orbits scale clipping above 2.0x
    • Electron paths are now dynamically updated when the parent widget updates
  • example app -

    • Added a new playground mode (web support will be added soon)

[1.2.0] - 2019-04-10 #

  • Added example app for demonstration
  • Updated documentation
  • Removed un-used test file (tests can be added in future)

[1.1.0] - 2019-04-10 #

  • Added documentation and example project.

[1.0.0] - 2019-04-09 #

  • Initial release, go create some atoms !!

example/lib/main.dart

// Copyright 2020 Abhishek Dubey. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.

import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';

import 'package:create_atom/create_atom.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        brightness: Brightness.dark,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyAtoms(),
    );
  }
}

class MyAtoms extends StatefulWidget {
  @override
  _MyAtomsState createState() => _MyAtomsState();
}

class _MyAtomsState extends State<MyAtoms> {
  static int _currentIndex = 1;

  static final List<Widget> _pages = [
    RandomAtoms(),
    AtomPlayground(),
  ];

  Widget _showText(text) {
    return Text(
      text,
      style: TextStyle(
        fontFamily: "Quicksand",
        fontSize: 100.0,
        color: Colors.white,
      ),
    );
  }

  Widget _bottomNavBar() {
    return BottomNavigationBar(
      currentIndex: _currentIndex,
      backgroundColor: Colors.black,
      unselectedItemColor: Colors.white,
      items: [
        BottomNavigationBarItem(
          icon: Icon(Icons.all_inclusive),
          title: Text("Random"),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.play_arrow),
          title: Text("Playground"),
        ),
      ],
      onTap: (int index) => setState(() => _currentIndex = index),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: PreferredSize(
        preferredSize: Size.fromHeight(150.0),
        child: SizedBox(
          height: 200.0,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              _showText("A "),
              _showText("T "),
              Atom(
                size: 75.25,
                orbitsColor: Colors.white,
                nucleusColor: Colors.white,
                electronsColor: Colors.white,
              ),
              _showText(" M"),
            ],
          ),
        ),
      ),
      body: _pages[_currentIndex],
      bottomNavigationBar: _bottomNavBar(),
    );
  }
}

class RandomAtoms extends StatefulWidget {
  @override
  _RandomAtomsState createState() => _RandomAtomsState();
}

class _RandomAtomsState extends State<RandomAtoms> {
  static final _rand = new Random();
  static int _r, _g, _b;

  int _genRand(int min, int max) => min + _rand.nextInt(max - min);

  _genColor() {
    return Color.fromRGBO(
      _r,
      _g,
      _b,
      1.0,
    );
  }

  _genRandColor() {
    _r = _genRand(0, 255);
    _g = _genRand(0, 255);
    _b = _genRand(0, 255);

    return _genColor();
  }

  Widget _atomGrid() {
    return GridTile(
      child: Atom(
        size: 129,
        orbitsColor: _genRandColor(),
        nucleusColor: _genRandColor(),
        electronsColor: _genRandColor(),
        orbit1Angle: (pi / 180) * _genRand(0, 361),
        orbit2Angle: (pi / 180) * _genRand(0, 361),
        orbit3Angle: (pi / 180) * _genRand(0, 361),
        animDuration1: Duration(milliseconds: _genRand(250, 5000)),
        animDuration2: Duration(milliseconds: _genRand(250, 5000)),
        animDuration3: Duration(milliseconds: _genRand(250, 5000)),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: GridView.count(
        crossAxisCount: 3,
        padding: EdgeInsets.all(4.0),
        children: <Widget>[
          _atomGrid(),
          _atomGrid(),
          _atomGrid(),
          _atomGrid(),
          _atomGrid(),
          _atomGrid(),
          _atomGrid(),
          _atomGrid(),
          _atomGrid(),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() {}),
        child: Icon(Icons.refresh),
        tooltip: "Randomize",
      ),
    );
  }
}

class AtomPlayground extends StatefulWidget {
  @override
  _AtomPlaygroundState createState() => _AtomPlaygroundState();
}

class _AtomPlaygroundState extends State<AtomPlayground> {
  double _scale = 200;
  double _oAngle1 = 0.0;
  double _oAngle2 = 1.047198;
  double _oAngle3 = 5.235988;
  Color _nColor = Color(0xffffffff);
  Color _oColor = Color(0xffffffff);
  Color _eColor = Color(0xffffffff);
  double _eSpeed1 = 1000.0;
  double _eSpeed2 = 2000.0;
  double _eSpeed3 = 3000.0;
  Duration _eDur1 = Duration(milliseconds: 1000);
  Duration _eDur2 = Duration(milliseconds: 2000);
  Duration _eDur3 = Duration(milliseconds: 3000);
  Widget _centerW;

  int _radioButton;

  void _changeNColor(Color colorNext) {
    setState(() => _nColor = colorNext);
  }

  void _changeOColor(Color colorNext) {
    setState(() => _oColor = colorNext);
  }

  void _changeEColor(Color colorNext) {
    setState(() => _eColor = colorNext);
  }

  _colorPicker(colorValue, colorFunction) {
    return showDialog(
      context: context,
      child: AlertDialog(
        titlePadding: EdgeInsets.all(0.0),
        contentPadding: const EdgeInsets.all(0.0),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(25.0),
        ),
        content: SingleChildScrollView(
          child: SlidePicker(
            pickerColor: colorValue,
            onColorChanged: colorFunction,
            paletteType: PaletteType.rgb,
            enableAlpha: false,
            displayThumbColor: true,
            showLabel: false,
            showIndicator: true,
            indicatorBorderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
          ),
        ),
      ),
    );
  }

  Widget _scaleSlider() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 10.0,
      children: [
        Text(
          "Scale :",
          style: TextStyle(color: Colors.white70),
        ),
        Container(
          width: 350.0,
          child: Slider(
            min: 21.5,
            max: 430,
            value: _scale,
            onChanged: (val) => setState(() => _scale = val),
          ),
        ),
      ],
    );
  }

  Widget _oAngleSlider1() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 10.0,
      children: [
        Text(
          "Orbit 1 :",
          style: TextStyle(color: Colors.white70),
        ),
        Container(
          width: 350.0,
          child: Slider(
            min: 0.0,
            max: 2 * pi,
            divisions: 360,
            value: _oAngle1,
            onChanged: (val) => setState(() => _oAngle1 = val),
          ),
        ),
      ],
    );
  }

  Widget _oAngleSlider2() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 10.0,
      children: [
        Text(
          "Orbit 2 :",
          style: TextStyle(color: Colors.white70),
        ),
        Container(
          width: 350.0,
          child: Slider(
            min: 0.0,
            max: 2 * pi,
            divisions: 360,
            value: _oAngle2,
            onChanged: (val) => setState(() => _oAngle2 = val),
          ),
        ),
      ],
    );
  }

  Widget _oAngleSlider3() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 10.0,
      children: [
        Text(
          "Orbit 3 :",
          style: TextStyle(color: Colors.white70),
        ),
        Container(
          width: 350.0,
          child: Slider(
            min: 0.0,
            max: 2 * pi,
            divisions: 360,
            value: _oAngle3,
            onChanged: (val) => setState(() => _oAngle3 = val),
          ),
        ),
      ],
    );
  }

  Widget _colorSelectors() {
    return Padding(
      padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
      child: Wrap(
        alignment: WrapAlignment.center,
        crossAxisAlignment: WrapCrossAlignment.center,
        runSpacing: 10.0,
        children: [
          Text(
            "Nucleus Color :",
            style: TextStyle(color: Colors.white70),
          ),
          VerticalDivider(),
          GestureDetector(
            onTap: () {
              _colorPicker(_nColor, _changeNColor);
            },
            child: Container(
              width: 30.0,
              height: 30.0,
              decoration: BoxDecoration(color: _nColor, shape: BoxShape.circle, border: Border.all(color: Colors.white)),
            ),
          ),
          VerticalDivider(),
          Text(
            "Orbits Color :",
            style: TextStyle(color: Colors.white70),
          ),
          VerticalDivider(),
          GestureDetector(
            onTap: () {
              _colorPicker(_oColor, _changeOColor);
            },
            child: Container(
              width: 30.0,
              height: 30.0,
              decoration: BoxDecoration(color: _oColor, shape: BoxShape.circle, border: Border.all(color: Colors.white)),
            ),
          ),
          VerticalDivider(),
          Text(
            "Electrons Color :",
            style: TextStyle(color: Colors.white70),
          ),
          VerticalDivider(),
          GestureDetector(
            onTap: () {
              _colorPicker(_eColor, _changeEColor);
            },
            child: Container(
              width: 30.0,
              height: 30.0,
              decoration: BoxDecoration(color: _eColor, shape: BoxShape.circle, border: Border.all(color: Colors.white)),
            ),
          ),
        ],
      ),
    );
  }

  Widget _eSpeedSlider1() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 10.0,
      children: [
        Text(
          "ESpeed 1 :",
          style: TextStyle(color: Colors.white70),
        ),
        Container(
          width: 350.0,
          child: Slider.adaptive(
            min: 50,
            max: 6000,
            value: _eSpeed1,
            onChanged: (val) => setState(() {
              _eDur1 = Duration(milliseconds: val.toInt());
              _eSpeed1 = val;
            }),
          ),
        ),
      ],
    );
  }

  Widget _eSpeedSlider2() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 10.0,
      children: [
        Text(
          "ESpeed 2 :",
          style: TextStyle(color: Colors.white70),
        ),
        Container(
          width: 350.0,
          child: Slider(
            min: 50,
            max: 6000,
            value: _eSpeed2,
            onChanged: (val) => setState(() {
              _eDur2 = Duration(milliseconds: val.toInt());
              _eSpeed2 = val;
            }),
          ),
        ),
      ],
    );
  }

  Widget _eSpeedSlider3() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      runSpacing: 10.0,
      children: [
        Text(
          "ESpeed 3 :",
          style: TextStyle(color: Colors.white70),
        ),
        Container(
          width: 350.0,
          child: Slider(
            min: 50,
            max: 6000,
            value: _eSpeed3,
            onChanged: (val) => setState(() {
              _eDur3 = Duration(milliseconds: val.toInt());
              _eSpeed3 = val;
            }),
          ),
        ),
      ],
    );
  }

  Widget _centerWidgetBar() {
    return Wrap(
      alignment: WrapAlignment.center,
      crossAxisAlignment: WrapCrossAlignment.center,
      children: <Widget>[
        Text(
          "Center Widget :",
          style: TextStyle(color: Colors.white70),
          textAlign: TextAlign.justify,
     overflow: TextOverflow.ellipsis,
        ),
        VerticalDivider(),
        Radio(
          value: 1,
          groupValue: _radioButton,
          onChanged: (val) {
            setState(() {
              _centerW = null;
              _radioButton = val;
            });
          },
        ),
        Text(
          "No",
          style: TextStyle(color: Colors.white70),
        ),
        VerticalDivider(width: 5.0),
        Radio(
          value: 2,
          groupValue: _radioButton,
          onChanged: (val) {
            setState(() {
              _centerW = Atom(
                size: 50,
                nucleusColor: _nColor,
                orbitsColor: _oColor,
                electronsColor: _eColor,
              );
              _radioButton = val;
            });
          },
        ),
        Text(
          "Atom",
          style: TextStyle(color: Colors.white70),
        ),
        VerticalDivider(width: 5.0),
        Radio(
          value: 3,
          groupValue: _radioButton,
          onChanged: (val) {
            setState(() {
              _centerW = Text(
                "Atom",
                style: TextStyle(color: _nColor),
              );
              _radioButton = val;
            });
          },
        ),
        Text(
          "Text",
          style: TextStyle(color: Colors.white70),
        ),
      ],
    );
  }

  void initState() {
    super.initState();
    _radioButton = 1;
  }

  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Center(
        child: Padding(
          padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
          child: Stack(
            children: [
              Align(
                alignment: Alignment.topCenter,
                child: Atom(
                  size: _scale,
                  orbit1Angle: _oAngle1,
                  orbit2Angle: _oAngle2,
                  orbit3Angle: _oAngle3,
                  nucleusColor: _nColor,
                  orbitsColor: _oColor,
                  electronsColor: _eColor,
                  animDuration1: _eDur1,
                  animDuration2: _eDur2,
                  animDuration3: _eDur3,
                  centerWidget: _centerW,
                ),
              ),
              Align(
                alignment: Alignment.bottomCenter,
                child: ClipRect(
                  child: BackdropFilter(
                    filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
                    child: Container(
                      height: 200,
                      decoration: BoxDecoration(
                        color: Colors.white.withOpacity(0.15),
                        borderRadius: BorderRadius.circular(10.0),
                      ),
                      child: SingleChildScrollView(
                        padding: EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 10.0),
                        physics: BouncingScrollPhysics(),
                        child: Column(
                          children: [
                            _scaleSlider(),
                            Divider(thickness: 2.0),
                            _oAngleSlider1(),
                            _oAngleSlider2(),
                            _oAngleSlider3(),
                            Divider(thickness: 2.0),
                            _colorSelectors(),
                            Divider(thickness: 2.0),
                            _eSpeedSlider1(),
                            _eSpeedSlider2(),
                            _eSpeedSlider3(),
                            Divider(thickness: 2.0),
                            _centerWidgetBar(),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

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


dependencies:
  create_atom: ^2.0.0

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:create_atom/create_atom.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
24
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
62
Learn more about scoring.

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

  • Dart: 2.8.4
  • pana: 0.13.15
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • create_atom that is a package requiring null.

Health suggestions

Format lib/create_atom.dart.

Run flutter format to format lib/create_atom.dart.

Format lib/src/create_atom.dart.

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

Format lib/src/electrons_anim.dart.

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

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.7.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.2
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test