introduce

Today we’ll take a look at Wired_Elements, a series of basic UI Elements with a hand-drawn look.

There is already a very mature component library on the Web side for this look-and-feel UI element, see here. It is based on rough. Js implementation of a series of components, can be used to quickly create interactive product design, there are already based on this design can drag and drop web side project software, you can search for a look, I have found before, but at that time did not collect… You can also use the UI for your blog, or just for fun. Anyway, the web side does have Flutter, but I don’t see Flutter. There is only a Flutter_rough project, which is a Flutter implementation of rough.js.

So today I will stand on my shoulders and write an implementation of Wired Elements, Flutter, above. I will pick one or two widgets and explain how to implement them later.

 

 

 

 

 

The giant flutter_rough

Flutter_rough stands on the shoulders of giants, but the author of this component library did not see the maintenance, and it seems that the demand for this style is not high. But we don’t care, just for fun! Because the latest flutter plug-ins all need null safety, but Flutter_rough is not null safety. Someone has already made an issue about this problem, and the author has not replied yet, so we have no way to introduce it through dependency. I had to copy the code into my own project and do null safety myself (which was noted in the wired_Elements readme for introducing Flutter_rough). Now that the basic work is done, we can start tapping into our component library of Wired_Elements.

Initialize the wired_Elements component library

First, we need to develop the Flutter&Dart package, so we follow the official steps to create the project step by step and do the initialization work.

1. Create a project:

flutter create --template=package wired_elements
Copy the code

2. implement package wired_elements: Wired_elements. Dart is exported from rough and SRC. Rough is the flutter_rough component library that has been copied and made null safety. Below the SRC folder are the handwritten widgets we need to implement. You can see that so far we have some basic widgets, but there are many that are missing such as calendars, progress bars, etc. We will continue to iterate. There is also a canvas folder under the SRC folder, which contains some general canvas operations to facilitate the development of our widgets. We will not go into details, but you can read the source code. Our main task is to introduce the specific hand written widgets.

Script button – wired_button

You can refer to the buttons on the Web side, which are mainly border and text. For text writing, we can directly introduce The Google Hand writing font, which is relatively simple, but how to realize the border of this script?

In fact, it is very simple. We hide the frame of the Flutter button and then wrap a layer of Container around the Flutter button. The custom decoration is already implemented by Flutter_rough, so we can use it.

@override Widget buildWiredElement() {return Container(padding: EdgeInsets. Zero, height: 42.0, decoration: RoughBoxDecoration( shape: RoughBoxShape.rectangle, borderStyle: RoughDrawingStyle( width: 1, color: borderColor, ), ), child: SizedBox( height: double.infinity, child: TextButton( style: TextButton.styleFrom( primary: textColor, ), child: child, onPressed: onPressed, ), ), ); }Copy the code

In the above code clip we use RoughBoxDecotation, which provides a specific border shape and style to choose from. We use rectangles and specify the thickness and color, so a simple Wired_button is implemented. All that is left is to add some parameters of the Flutter button itself to expose it.

If you look at the wired_button source code, we don’t inherit StatefulWidget or StatelessWidget directly, we write WiredBaseWidget that inherits StatelessWidget, Wired_button then inherits the WiredBaseWidget and implements the WiredBaseWidget method buildWiredElement().

Why do you do that? It can be seen that in the WiredBaseWidget class, we have wrapped a layer of RepaintBoundary, which is used to isolate the redrawing of the screen canvas. Because we use a custom decoration, we inherit BoxPainter and use the same canvas. In this way, whenever this custom dcoration is used on the screen, the same canvas instance is used to draw the screen. If there are multiple Wired_buttons on the screen, when we click a certain button, it will trigger redrawing. If we do not isolate redrawing, other buttons will also be redrawn. This was not what we expected, so we used the RepaintBoundary to avoid this problem.

Script slider – wired_slider

Sliders can be used to adjust screen brightness, video playback progress, volume and so on. Refer to the Slider of the Flutter material.

So how to achieve the handwriting slider, here our idea is: Hide the progress bar line and the current position solid circle of the Flutter Slider by setting the activeColor and inactiveColor colors to transparent. This way we can overlay the current position with handwritten lines and circles, achieving the UI script style. How to cover? Stack layout.

@override Widget build(BuildContext context) { return Stack( alignment: Alignment.center, children: [// SizedBox(height: 1, width: double. Infinity, child: WiredCanvas(painter: WiredLineBase(x1: 0, y1: WiredCanvas)) 0, x2: double.infinity, y2: 0, strokeWidth: 2, ), fillerType: HatchFiller,),), // The position of the Slider circle (left: _getSliderWidth() * _currentSliderValue/widget. max-12, child: SizedBox(height: 24.0, width: 24.0, child: WiredCanvas( painter: WiredCircleBase( diameterRatio: .7, fillColor: textColor, ), fillerType: RoughFilter HachureFiller, fillerConfig: fillerConfig. Build (hachureGap: 1.0),),),), SliderTheme (data: SliderThemeData( trackShape: CustomTrackShape(), ), child: Slider( value: _currentSliderValue, min: widget.min, max: widget.max, activeColor: Colors.transparent, inactiveColor: Colors.transparent, divisions: widget.divisions, label: widget.label, onChanged: (value) { bool result = false; if (widget.onChanged != null) { result = widget.onChanged!(value); } if (result) { setState(() { _currentSliderValue = value; }); } }, ), ), ], ); }Copy the code

In the above code, we used the Stack layout and assigned alignment center to override the original Slider.

Because the Flutter Slider has a default TAB, we didn’t want it to be anything like that. The lines we were overriding would be from beginning to end, and the margins would be longer than the one we were overriding, so we used a SliderTheme to wrap the Flutter Slider. And then we’re going to implement data custom, why would we remove the Margins? Please refer here.

We have drawn a handwritten UI, but the Slider can change its value. In other words, the solid circle of the Slider can change its position, so the circle must be changed to the correct position as it is dragged. We know the current value of the Slider _currentSliderValue, and we know the maximum value of the Slider widget. Max. If we know the Slider’s physical pixel sliderWidth, X = sliderWidth * _currentSliderValue/widget. Max (sliderWidth * _currentSliderValue/widget. Max)

double _getSliderWidth() {
double width = 0;
try {
  var box = context.findRenderObject() as RenderBox;
  width = box.size.width;
} catch (e) {}

return width;
}
Copy the code

Some students will ask, look at the source code why subtract 12? Because the diameter of the solid ball is 24!

Wait a minute! Why is there such code in the initState method in the source code? As noted in the comment, the sliderWidth is not available the first time we enter the build method, so we need to call the setState method after the first build to force the widget to build again, so that the sliderWidth is available for initialization.

// Delay for calculate the slider's width `_getSliderWidth()` during the next frame
Future.delayed(Duration(milliseconds: 0), () {
  setState(() {});
});
Copy the code

At the end

Wired_button and Wired_slider are two representative widgets. Other widgets that have been implemented are basically the same. The main idea is to remove the existing borders or lines and use calligraphy to cover the existing UI. So the code UI style changes. Source code is here, pub.dev is here, welcome everyone to raise PR or issues, if you help hope to be able to give a star, thank you!!