I am participating in the Mid-Autumn Festival creative submission contest, please see the details

The clock is an amazing thing that can be used as an entry point for creative implementations, such as the little creative clock that the genie created with Compose

Look at the effect

Used to Compose such a combination of statement development method called hot plug, save a lot of code, simplify the development, especially the Modifier magic thing, familiar with the solution to all types of problems.

Read his source code, very simple, about 100 lines, not to mention, with Flutter copy. Or if you want to go straight to GitHub, criticism is welcome

The environment used in this case is:

Flutter (Channel stable, 2.2.3, on macOS 11.0.1 20B29 Darwin-arm, Locale useful - Hans - CN), Flutter version 2.2.3 the at/Users/moon/Documents/SDK/Flutter, Framework revision f4abaa0735 (2 months ago), 2021-07-01 12:46:11-0700 • Engine Revision 241C87AD80 • Dart Version 2.13.4 • Pub Download Mirror https://pub.flutter-io.cn • Flutter Download mirror https://storage.flutter-io.cnCopy the code

1. Create a time cube. A common Container contains a text

_Number() { return Container( width: 40, height: 40, child: Text( "5", style: TextStyle(color: Colors.white, fontSize: 16),),); }Copy the code

It can be designed by looking at the GIF above, the small box is stateful, is the current point in time if the background color changes, also can make the number of font larger, different from other numbers, and it will be in the column in the middle of the screen. So let’s just encapsulate this little cell.

_Number(int number, bool isActive) {
  var backgroundColor;
  var numberSize;
  if (isActive) {
    backgroundColor = Colors.black;
    numberSize = 18.0;
  } else {
    backgroundColor = Colors.deepPurpleAccent;
    numberSize = 16.0;
  }

  return Container(
    alignment: Alignment.center,
    width: _normalSize,
    height: _normalSize,
    child: Text(
      "$number",
      style: TextStyle(color: Colors.white, fontSize: numberSize),
    ),
  );
Copy the code

2 Build a list of cells

Compose is composed of several columns of numbers, so you can draw one of them and transfer different numbers and states to the other ones. Compose uses column, Flutter also has the same thing, of course, listView or other containers can be used.

child: Column(
  children: [
      _Number(1 , false,),
      _Number(2 , false,),
      _Number(3 , false,),
      _Number(4 , false,),
      _Number(5 , true,),
  ],
))
Copy the code

For aesthetic purposes, you can cut a rounded corner. The modifier for compose reflects the advantage, which can clip the column directly, but the Flutter is not so powerful, so the Flutter can only be treated individually according to its position.

if (number == 0) {
  borderRadius = BorderRadius.only(
      topLeft: Radius.circular(15), topRight: Radius.circular(15));
} else if (number == (totalSize - 1)) {
  borderRadius = BorderRadius.only(
      bottomLeft: Radius.circular(15), bottomRight: Radius.circular(15));
} else {
  borderRadius = BorderRadius.all(Radius.zero);
}
Copy the code
decoration: BoxDecoration(
  color: backgroundColor,
  borderRadius: borderRadius,
),
Copy the code

Perhaps there is a simple way to set the corner, please friends tips.

3. Construct a multi-column cell

In the same way, create six columns representing the time and seconds of the two columns, wrap the six columns with row, center the child widgets of the outer container, accept the current time as the state, and highlight them. For example, it’s 21:49:15

This gives you a separate static time scale.

4 about the Align

This graph doesn’t seem to be right, a sense of chaos that requires you to be able to see the current time at a glance, that is, align all The Times in a line.

This aspect Compose takes advantage of the modifier offset can be easily realized displacement.

val mid = (range.last - range.first) / 2f 
val offset = 40.dp * (mid - current)
Modifier .offset(y = offset)
Copy the code

Flutter does not seem to be so convenient. How to do this? I use the Align component.

The Align component can adjust the position of its children and determine its own width and height based on the width and height of the children, as defined below:

Align({
  Key key,
  this.alignment = Alignment.center,
  this.widthFactor,
  this.heightFactor,
  Widget child,
})
Copy the code
  • alignment: Need oneAlignmentGeometryType that represents the starting position of the child component in the parent.AlignmentGeometryIs an abstract class that has two commonly used subclasses:AlignmentandFractionalOffset.
  • widthFactorandheightFactorIs used to determineAlignThe width and height property of the component itself; These are two scaling factors, multiplied by the width and height of the child element, and the final result isAlignWidth and height of the component. If the value isnull, the width and height of the component will take up as much space as possible.

For example, the following

Container(height: 120.0, width: 120.0, color: Colors. Blue [50], child: Align(alignment: Align. TopRight, child: FlutterLogo( size: 60, ), ), )Copy the code

I explicitly specify that the Container is 120 in width and 120 in height. If I do not specify width and height explicitly, I can achieve the same effect by specifying both widthFactor and heightFactor to be 2:

Align(
  widthFactor: 2,
  heightFactor: 2,
  alignment: Alignment.topRight,
  child: FlutterLogo(
    size: 60,
  ),
),
Copy the code

5 Align the time line

Align actually expands the height to a multiple of the previous heightFactor, but its child widget is still at the top of Align, and the distance from the current time point to the top of column is known, so the current time point is positioned in the middle of Align. Need to complete (or cut off) the space below the current point in time. For clarity, I colored both the overall background and the Align background.

The first column: hours first, only three grid, the current is 2, 2 in the third, so you need to give up below two grid, the second column: hours of bottom, a total of 10 squares, the current number is 2, is located in the third, in order to make up and down is equal, will need the following cut off 4 Numbers, let 2 is located in the middle position.

And so on.

So you can see the pattern here. The coefficient factor of height is:

(current * 2 + 1)/numbers.length
Copy the code

Numbers. Length is the length of the column. Therefore, the function for each column can be written as

_columnNumber(List<int> numbers, int current) {
  List<Widget> list = [];
  numbers.forEach((e) {
    list.add(_Number(e, e == current, numbers.length));
  });

  return Container(
    color: Colors.white,
    child: Align(
      alignment: Alignment.topCenter,
      widthFactor: 1,
      heightFactor: (current *2 + 1)/numbers.length,
      child: Container(
        height: numbers.length * _normalSize,
        margin: EdgeInsets.only(left: 5, right: 5),
        child: Column(
          children: list,
        )),
    ),
  );
}
Copy the code

At this point a static time clock is drawn.

6 move

Get the current time, start the timer, refresh once per second to achieve dynamic effect.

The complete code

Conclusion: Compose, Flutter and SwiftUI are all declarative UIs. Good design has been established as the trend of development. At the same time, it is easy to get started and can focus more on business.

I only wrote the small demo for one night, which was quite hasty. Many details need to be improved, such as the animation is a little stiff, and the code details are not good enough. Friends who are interested in it need to correct and supplement.

In fact, what we lack most is creativity.

Have a nice weekend