After a short period of contact with Flutter, there is a problem that has always been on the surface, that is, the Widget in Flutter is indeed not as useful as the controls in Android. In Android, such as TextView, ImageView, etc. or other Views, they all have their own very wide properties and methods, such as width, height, margin and padding, and related click events. In Flutter, the corresponding controls, these basic and commonly used attributes are missing, so that every time a widget is written, if you want to implement click events, or margin or padding, you have to use other widgets to wrap a layer, which is very inconvenient to use. Based on the above background, the idea of encapsulating the base class came into being.
Although I have been in contact with Flutter before, I haven't used it for a long time. I can't help but find some shortcomings when I pick it up now. If there are any problems with the packaging, I hope I will give you some advice.
1. What attributes need to be encapsulated
What attributes are needed in detail? It is not that the more, the better, nor that the fewer, the better. Instead, it only needs to expand the commonly used ones based on actual development needs.
What are the text or picture controls or other controls that we need to consider in actual development? Is it the most common thing to be its own width and height? This is the most common and necessary. In addition to its width and height, its own click events are also a property with constant frequency. Therefore, in the base class widget, its width, height and click events must exist. Speaking of events, in addition to click events, some double-click or long-press events also exist in some requirements. Therefore, try to block them into the base class to facilitate the use of subclass controls.
In addition, external margins and inner margins are also essential properties. I dare not say that nine of the ten controls are used, at least more than half of the probability, so this also needs to be encapsulated into the base class; as for background properties, such as rounded, rounded, hollow, and solid, these depend on actual projects, and can also be placed in the base class if needed.
I have listed it in advance and the general properties of the packaging are as follows. Of course, everyone's packaging is different, and it mainly depends on the actual needs.
property | type | Overview |
---|---|---|
width | double | Width |
height | double | high |
margin | double | Unified margin settings (upper left, lower right) |
marginLeft | double | Margin (left) |
marginTop | double | Margin (Part 1) |
marginRight | double | Margin (right) |
marginBottom | double | Margin (Part 2) |
padding | double | Unified margin settings |
paddingLeft | double | Inner margin (left) |
paddingTop | double | Inner margin (Part 1) |
paddingRight | double | Inner margin (right) |
paddingBottom | double | Inner margin (bottom) |
onClick | method | Click Event |
onDoubleClick | method | Double-click event |
onLongPress | method | Long press event |
backgroundColor | Color | Background color and decoration |
strokeWidth | double | The uniform width of the background border |
strokeColor | Color | Background border color |
solidColor | Color | Background fill color |
radius | double | Background angle, unified setting |
leftTopRadius | double | The upper left angle of the background |
rightTopRadius | double | The upper right angle of the background |
leftBottomRadius | double | The background lower left angle |
rightBottomRadius | double | The lower right angle of the background |
isCircle | bool | Is the background round? |
childWidget | Widget | The passed child control |
alignment | Alignment | Location |
gradientColorList | List | Gradient color collection |
gradientColorStops | List | Gradient color value gradient, value range [0,1] |
gradientBegin | Alignment | Gradient start position |
gradientEnd | Alignment | Gradient end position |
2. Determine the base class Widget
The base class widget mainly determines the following aspects: the first is to customize an abstract class or a non-abstract class, the second is to inherit the method, adopt stateful or stateless, and the third is to implement the click method of the component.
At the beginning, I wrote an abstract base class. After all, in the following operations, I will encapsulate each control again on a native basis instead of using it independently. In this case, abstract classes are the most suitable, and I can expand the methods that must be implemented to the subclass. However, there is a disadvantage in this case, that is, native controls cannot enjoy the various properties of this base class, and there is no way, and finally they are changed to non-abstract classes. In this way, both methods can be satisfied.
Regarding the inheritance method, for a page, it is more or less necessary to render data and update the UI. In this case, it is certain to inherit StatefulWidget, but generally, someone else triggers it, but it rarely triggers it on its own initiative. Therefore, generally speaking, we can inherit StatelessWidget.
Regarding the click method of components, if it is not the Button level, few controls come with click events, so we have to implement them by ourselves. Flutter provides many components that can help implement clicks, such as InkWell, GestureDetector, InkResponse, and original pointer event Listener, all provide us with rich touch events. Here is a brief list:
InkWell
InkWell( onLongPress: (){ print("Long press event"); }, onDoubleTap: (){ print("Double-click event"); }, onTap: (){ print("Click Event"); } child: Container() )
GestureDetector
return GestureDetector( child: const Text("front page"), onLongPress: (){ print("Long press event"); }, onDoubleTap: (){ print("Double-click event"); }, onTap: (){ print("Click Event"); }, onPanDown: (DragDownDetails detail) { // The position relative to the screen when the finger is pressed print("Press the callback"); }, onPanUpdate: (DragUpdateDetails detail) { print("Finger Slide Callback"); }, onPanEnd: (DragEndDetails detail) { print("Finger stops sliding callback"); }, //Drag the event vertically onVerticalDragUpdate: (DragUpdateDetails details) { }, // Horizontal drag event onHorizontalDragUpdate: (DragUpdateDetails details) { }, );
InkResponse
return InkResponse( child: const Text("Click"), onTap: () { //Click event print("Click Event"); }, onLongPress: () { //Long press event print("Long press event"); }, onDoubleTap: () { //Double-click event print("Double-click event"); }, );
Original pointer event
return Listener( child: Container( child: const Text("test"), ), //Press the callback with your finger onPointerDown: (PointerDownEvent event) {}, //Finger move callback onPointerMove: (PointerMoveEvent event) {}, //Finger lifts up to call back onPointerUp: (PointerUpEvent event) {}, //Touch event cancel callback onPointerCancel: (PointerCancelEvent event) {}, );
There are many related attributes. You can check the relevant source code. Which one is used? I think the first three are OK. After all, there are related clicks, double-clicks, long-press events. If you want to get more touch events, you can use GestureDetector. If you just click, long-press and double-click, InkWell is recommended. It is relatively sensitive to clicks. Of course, it depends on yourself which one to use.
III. Base class implementation
The base class implementation is relatively simple. The outermost layer of the build method is wrapped with a click event, and then the Container component is wrapped with a width and height, margin, padding and background implementation. The corners, circles and gradients are used to describe the property decoration of the Container.
All source codes are as follows, all system API calls, nothing particularly difficult.
import 'package:flutter/'; ///AUTHOR:AbnerMing ///DATE:2023/5/11 ///INTRODUCE: Control stateless base classclass BaseWidget extends StatelessWidget { final VoidCallback? onClick; //Click event final VoidCallback? onDoubleClick; //Double-click event final VoidCallback? onLongPress; //Long press event final double? width; //width final double? height; //high final double? margin; //Outer margin, upper left, lower right final double? marginLeft; //Outside margin, distance to the left final double? marginTop; //Outer margin, distance from the upper edge final double? marginRight; //Outer margin, distance to the right final double? marginBottom; //The margin, the distance below final double? padding; //Inner margin, upper left, lower right final double? paddingLeft; //Inner margin, distance to the left final double? paddingTop; //Inner margin, distance from the upper edge final double? paddingRight; //Inner margin, distance to the right final double? paddingBottom; //Inner margin, distance from below final Color? backgroundColor; //Background color and decoration take one of them final double? strokeWidth; //The uniform width of the background border final Color? strokeColor; //The color of the background border final Color? solidColor; //Background fill color final double? radius; //The background angle final bool? isCircle; //Is the background round? final double? leftTopRadius; //The upper left angle of the background final double? rightTopRadius; //Background upper right angle final double? leftBottomRadius; //Background lower left angle final double? rightBottomRadius; //Background lower right angle final Widget? childWidget; //Subcontrol final Alignment? alignment; //Location final int? gradient; //Gradial mode, int type is used to support subsequent expansion final List<Color>? gradientColorList; //Gradial color final List<double>? gradientColorStops; //Color value gradient, value range [0,1] final Alignment? gradientBegin; //The starting position of the gradient final Alignment? gradientEnd; //Gradial end position //Border color const BaseWidget( {, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , }); @override Widget build(BuildContext context) { return InkWell( highlightColor: , // Transparent color splashColor: , // Transparent color onTap: onClick, onDoubleTap: onDoubleClick, onLongPress: onLongPress, child: Container( width: width, height: height, alignment: alignment, margin: margin != null ? (margin!) : ( left: marginLeft != null ? marginLeft! : 0, top: marginTop != null ? marginTop! : 0, right: marginRight != null ? marginRight! : 0, bottom: marginBottom != null ? marginBottom! : 0), padding: padding != null ? (padding!) : ( left: paddingLeft != null ? paddingLeft! : 0, top: paddingTop != null ? paddingTop! : 0, right: paddingRight != null ? paddingRight! : 0, bottom: paddingBottom != null ? paddingBottom! : 0, ), color: backgroundColor, decoration: backgroundColor != null ? null : getDecoration(), child: childWidget ?? getWidget(context), )); } /* * Get Decoration * */ Decoration? getDecoration() { BorderRadiusGeometry? borderRadiusGeometry; if (radius != null) { //All angles borderRadiusGeometry = ((radius!)); } else { // Otherwise, it is, from all angles borderRadiusGeometry = ( topLeft: (leftTopRadius != null ? leftTopRadius! : 0), topRight: (rightTopRadius != null ? rightTopRadius! : 0), bottomLeft: (leftBottomRadius != null ? leftBottomRadius! : 0), bottomRight: ( rightBottomRadius != null ? rightBottomRadius! : 0)); } Gradient? tGradient; if (gradient != null) { tGradient = LinearGradient( colors: gradientColorList != null ? gradientColorList! : [], // What are the gradient colors? begin: gradientBegin != null ? gradientBegin! : , // The location where the gradient color begins, default centerLeft end: gradientEnd != null ? gradientEnd! : , // The position where the gradient color ends, default centerRight stops: gradientColorStops, // Color value gradient, value range [0,1], the length should be the same as the length of colors ); } Decoration? widgetDecoration = BoxDecoration( gradient: tGradient, //Background color color: solidColor != null ? solidColor! : , //Radical corner radius borderRadius: isCircle == true ? null : borderRadiusGeometry, //Is it a circle shape shape: isCircle == true ? : , //Border line width and color border: ( width: strokeWidth != null ? strokeWidth! : 0, color: strokeColor != null ? strokeColor! : ), ); return widgetDecoration; } /* * Get the control * */ Widget? getWidget(BuildContext context) { return null; } }
Specific use
There are two ways to use it. One is to use it directly. Just use BaseWidget to wrap your components, and the relevant attributes and methods can be called directly.
return BaseWidget( childWidget: const Text("Test text"), margin: 10, onClick: () { //Click event }, );
The second type is to define the components yourself, inherit the BaseWidget, and expand the properties you want to implement, and then use the components you define directly. For example, I want to customize a Text, as shown below:
class SelfText extends BaseWidget { final String? text; const SelfText(, {, , , , , , , , , , , , , , , , , , , , , , , , , , , }); @override Widget? getWidget(BuildContext context) { return Text(text!); } }
When using it specifically, use it directly, you don’t need to wrap the BaseWidget on the outer layer, and you can also expand your properties at will in your custom class.
return SelfText( "Test text", margin: 10, onClick: () { //Click event }, );
4. Related summary
In actual development, it is still necessary to exist for the base class of Widget, otherwise there will be a lot of redundant nested code. How to encapsulate it depends on the relevant needs and business. OK, Iron Man, this article ends here. I hope it can help you no matter whether the packaging is good or bad.
The above is the detailed content of the implementation of the Widget base class for Flutter control. For more information about Flutter Widget, please follow my other related articles!