Padding
Padding can add padding (leave blank) to child nodes, which is similar to the effect of margin. It is defined as follows:
Padding({ ... EdgeInsetsGeometry padding, Widget child, }) Copy code
EdgeInsetsGeometry is an abstract class. In development, we generally use EdgeInsets class. It is a subclass of EdgeInsetsGeometry and defines the method of setting filling
EdgeInsets
- From ltrb (double left, double top, double right, double bottom): specify the filling in four directions respectively
- all(double value): all directions are filled with the same value
- Only ({left, top, right, bottom}): you can set the filling in a specific direction, and you can specify multiple directions at the same time
- Symmetric ({vertical, horizontal}): used to set the filling direction of symmetry
Chestnuts
class PaddingTest extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("padding"), ), body: Container( color: Colors.red, child: Padding( padding: EdgeInsets.fromLTRB(5, 10, 15, 20), child: Text("I am 345"), ), ), ); } } Copy code
Size restricted containers
Size class is used to limit the size of containers. Many such attributes are provided in fluent, such as ConstrainedBox, SizedBox, UnconstrainedBox, AspectRatio, etc.
ConstrainedBox
ConstrainedBox is used to add additional constraints to subcomponents. For example, set the minimum height, etc
Chestnuts
class BoxTest extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Box"), ), body: getConstrainedBox(), ); } Widget getConstrainedBox() { return ConstrainedBox( constraints: BoxConstraints(minWidth: double.infinity, minHeight: 50), child: DecoratedBox( decoration: BoxDecoration(color: Colors.red), ), ); } } Copy code
You can see that although the height of the Container is set to 10, the final display is 50 pixels. This is the minimum height of the ConstrainedBox, which takes effect
BoxConstraints
const BoxConstraints({ this.minWidth = 0.0, //Minimum width this.maxWidth = double.infinity, //Maximum width this.minHeight = 0.0, //Minimum height this.maxHeight = double.infinity //Maximum height }) Copy code
BoxConstraints defines some convenient constructors for generating BoxConstraints quickly
- Generated size limit (GTH):
- expand(): you can generate BoxConstraints as large as possible
- There are others, as shown in the figure:
SizedBox
Used to fix the width and height of child elements, for example:
Widget getSizedBox() { return SizedBox(width: 50, height: 50, child: getRedBackground()); } Widget getRedBackground() { return DecoratedBox( decoration: BoxDecoration(color: Colors.red), ); } Copy code
In fact, SizedBox is just a customization of ConstrainedBox
@override RenderConstrainedBox createRenderObject(BuildContext context) { return RenderConstrainedBox( additionalConstraints: _additionalConstraints, ); } BoxConstraints get _additionalConstraints { return BoxConstraints.tightFor(width: width, height: height); } Copy code
From the above source code, we can see that both Sized and ConstrainedBox are generated through RenderConstrainedBox, and their createRenderObject method returns a RenderConstrainedBox
Multiple restrictions
If a component has more than one parent ConstrainedBox, which one will take effect in the end? For example:
Widget getConstrainedBoxS() { return ConstrainedBox( constraints: BoxConstraints(minWidth: 90, minHeight: 50), child: ConstrainedBox( constraints: BoxConstraints(minWidth: 50, minHeight: 90), child: getRedBackground(), ), ); } Copy code
It can be seen from the above width and height that for minWidth and minHeight, the corresponding value between father and son is larger. In fact, only in this way can we ensure that the parent restriction does not conflict with the child restriction
UnconstrainedBox
This component will not impose any restrictions on sub components. It allows sub components to be drawn according to their own size. Generally, we rarely use this component, but it may be helpful to remove multiple restrictions, as follows:
Widget getUnConstrainedBox() { return ConstrainedBox( constraints: BoxConstraints(minWidth: 90, minHeight: 50), //Remove parent restrictions child: UnconstrainedBox( child: ConstrainedBox( constraints: BoxConstraints(minWidth: 50, minHeight: 90), child: getRedBackground(), ), ), ); } Copy code
You can see that the parent restriction above has been canceled, and the final display is 50x50.
However, it should be noted that this restriction is actually removed. It can be seen from the figure that there is still blank left and right, that is, the parent restriction exists, but it does not affect the size of the child element getRedBackground(), but it still occupies the corresponding space. This must be noted.
So is there any way to completely remove restrictions? The answer is no! Therefore, if you want to limit the sub components in development, you should pay attention to it, because once you limit the specified conditions, it may be very difficult to customize the size of the sub components!
In the actual development, when we find that SizedBox or ConstrainedBox has been used to give the width and height of the sub element, but it still has no effect, we can almost conclude that there are restrictions set on the parent element!
For example, in the right menu of AppBar in the Material component, we use SizedBox to specify the size of the loading button. The code is as follows:
AppBar( title: Text("Box"), actions: [ SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation(Colors.white70), ), ) ], ), Copy code
It can be seen that the loading does not change because of the set size. This is because the action restriction condition has been specified in the Appbar. Therefore, we need to remove the restriction according to the defined size of laoding, as follows:
actions: [ UnconstrainedBox( child: Padding( padding: EdgeInsets.all(10), child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation(Colors.white70), ), ), ), ) ], Copy code
Padding is used above, and an inner margin is taken to prevent pasting on the right side of the screen
Other container restriction classes
In addition to the containers described above, there are other size limited containers, such as:
- AspectRatio: you can know the aspect ratio of sub components
- LimitedBox: used to specify the maximum width and height
- FractionallySizedBox can set the width and height of child components according to the width and height ratio of parent container,
Because these are relatively simple to use, you can understand them by yourself
Decorative container
DecoratedBox
DecoratedBox can draw some decorations before (or after) its sub components, such as background, border, gradient, etc. the definitions are as follows:
const DecoratedBox({ Decoration decoration, DecorationPosition position = DecorationPosition.background, Widget child }) Copy code
- Decoration: represents the decoration to be drawn. Its type is decoration. It is an abstract class that defines an interface createBoxPainter(). The main responsibility of the subclass is to create a brush by implementing it, which is used to draw decoration.
- position: this attribute determines where to draw the Decoration. It accepts the enumeration type of decorationposition, which has two types:
- background: draw after subcomponents
- Foreground: draw on the sub components, that is, the foreground
BoxDecoration
We usually directly use BoxDecoration class, which is a subclass of Decoration and realizes the drawing of common decorative elements
BoxDecoration({ Color color, //colour DecorationImage image,//picture BoxBorder border, //frame BorderRadiusGeometry borderRadius, //fillet List<BoxShadow> boxShadow, //Shadows, you can specify multiple Gradient gradient, //Gradual change BlendMode backgroundBlendMode, //Background blending mode BoxShape shape = BoxShape.rectangle, //shape }) Copy code
Below is a button to realize the gradient of background color with shadow
class DecoratedTest extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("DecoratedTest"), ), body: Container( child: DecoratedBox( //Background: background, foreground: foreground position: DecorationPosition.background, decoration: BoxDecoration( //background gradient gradient: LinearGradient(colors: [Colors.red, Colors.orange[700]]), //fillet borderRadius: BorderRadius.circular(10), //shadow boxShadow: [ BoxShadow(color: Colors.black26, offset: Offset(2, 2)) ]), child: Padding( padding: EdgeInsets.symmetric(horizontal: 80, vertical: 20), child: Text( "Login", style: TextStyle(color: Colors.white), ), ), ), ), ); } } Copy code
DecoratedBox can be used to decorate sub elements. In the above example, gradient, fillet, shadow, etc. are used for decoration. The effects are as follows:
In fact, the function of DecoratedBox is similar to that of shap in android, which adds various styles to the control.
Transform transform
Transform can apply some matrix transformations to its sub components when drawing to realize some special effects.
Matrix4 is a 4D matrix, through which we can realize various rectangle operations, for example:
class TransformTest extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Transform"), ), body: Container( color: Colors.black, child: getSkewTransform(), ), ); } Widget getSkewTransform() { return Transform( alignment: Alignment(-1, -1), // transform: Matrix4.skewY(0.3), //Tilt 0.3 radians along the Y axis child: Container( padding: const EdgeInsets.all(8), color: Colors.deepOrange, child: const Text('Apartment for rent!'), ), ); } } Copy code
translation
Transform.translate accepts an offset parameter, which can translate the specified distance of the sub components along the x and y axes during drawing
//translation Widget getTranslate() { return DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: Transform.translate( offset: Offset(10, 10), child: Text("hello world"), ), ); } Copy code
rotate
Transform.rotate can rotate and change sub components
Widget getRotate() { return DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: Transform.rotate( angle: math.pi / 2, //Rotate 90 degrees child: Text("hello world"), ), ); } Copy code
Note that math needs a guide bag
import 'dart:math' as math; Copy code
zoom
Transform.scale can reduce or enlarge subcomponents
Widget getScale() { return DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: Transform.scale( scale: 1.5, child: Text("hello world"), ), ); } Copy code
be careful
The change of Transform is in the drawing stage, not in the layout stage. Therefore, no matter what changes are made to the sub components, the size of the space occupied and the position on the screen remain unchanged, because these are determined in the layout stage, for example:
Widget getTest() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ getScale(), Text( "Hello", style: TextStyle(color: Colors.green, fontSize: 18), ) ], ); } Copy code
Since the space occupied by the Text in getScale is still red after it is enlarged, the second Text will be next to the red part and will eventually coincide
Since matrix transformation only works in the drawing stage, in some scenarios, when the UI needs to be changed, the visual UI change can be achieved through matrix transformation instead of re build ing the process, which will save the cost of layout, so the performance will be better. For example, the Flow component uses matrix transformation to update the UI internally. In addition, Transform is also widely used in the animation component of fluent to improve performance
RotatedBox
RotatedBox and transform The function of rotate is similar, but there is one difference: the change of RotatedBox is in the layout stage, which will affect the position and size of sub components
Modify the chestnuts above:
Widget getTest() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ getRotate(), Text( "Hello", style: TextStyle(color: Colors.green, fontSize: 18), ) ], ); } Widget getRotate() { return DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: RotatedBox( quarterTurns: 1, //Rotate 90 degrees (1 / 4) child: Text("hello world"), ), ); } Copy code
Because the RotatedBox acts on the layout stage, the sub components will rotate 90 degrees (instead of drawing content), and the decoration will act on the actual space occupied by the sub components, so the final effect is as shown in the figure above
Container
The container component has been used many times before. Container is a composite container, which does not correspond to a specific RenderObject. It is a multi-functional container composed of components such as DecoratedBox, ConstrainedBox, Transform, Padding and Align
Therefore, we only need a Container component to realize the scenes of decoration, change and restriction at the same time.
Container({ this.alignment, this.padding, //The white filling in the container belongs to the decoration scope of decoration Color color, // Background color Decoration decoration, // Background decoration Decoration foregroundDecoration, //Foreground decoration double width,//Width of container double height, //Height of container BoxConstraints constraints, //Restrictions on container size this.margin,//The white filling outside the container does not belong to the decoration scope of decoration this.transform, //Transform this.child, }) Copy code
Most of the properties of Container have been introduced, but there are two points to be mentioned:
- The size of the Container can be specified by the width and height attributes, or by constraints; If both exist, width and height take precedence. In fact, a constraint will be generated inside the Container according to width and height
- Color and decoration are mutually exclusive. If they are specified at the same time, an error will be reported! In fact, when color is specified, a decoration is automatically created in the Container
Chestnuts
class ContainerTest extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Container"), ), body: Container( margin: EdgeInsets.only(top: 50, left: 120), constraints: BoxConstraints.tightFor(width: 200, height: 150), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), //Background radial gradient gradient: RadialGradient( colors: [Colors.red, Colors.orange], center: Alignment.topLeft, radius: 58), //Card shadow boxShadow: [ BoxShadow( color: Colors.black54, offset: Offset(2, 2), blurRadius: 12), ]), child: Text("521", style: TextStyle(color: Colors.white, fontSize: 40)), transform: Matrix4.rotationZ(.2), alignment: Alignment(0,0), ), ); } } Copy code
It can be seen that the contractor has the functions of multiple components. It can be seen from the source code that it is the combination of multiple components we introduced earlier. In fluent, the Container component is also an example in which combination takes precedence over inheritance
Padding and Margin
Container( margin: EdgeInsets.all(20.0), //White filling outside the container color: Colors.orange, child: Text("Hello world!"), ), Container( padding: EdgeInsets.all(20.0), //Whitening in container color: Colors.orange, child: Text("Hello world!"), ), Copy code
The effect is similar to that of padding/margin in Android. Padding is the inner margin and margin is the outer margin
In fact, margin and padding in the Container are implemented through the padding component. The above code is actually equivalent to the following code:
Padding( padding: EdgeInsets.all(20.0), child: DecoratedBox( decoration: BoxDecoration(color: Colors.orange), child: Text("Hello world!"), ), ), DecoratedBox( decoration: BoxDecoration(color: Colors.orange), child: Padding( padding: const EdgeInsets.all(20.0), child: Text("Hello world!"), ), ), Copy code
It's actually a Padding for the outermost layer
Scaffold, TabBar, bottom navigation
A complete routing page may include navigation bar, drawer menu and bottom Tab navigation bar menu. If each routing page needs to be completed manually by the developer, it will be a boring and troublesome thing.
Fortunately, some ready-made components are provided in the fluent material component library to reduce our development tasks
Scaffold
Scaffold is the skeleton of a routing page, which can easily assemble a complete page
We implement a page that contains
1. Navigation bar, buttons of navigation bar
2. Drawer menu
3. Bottom navigation
4. Suspension button at the lower right corner
The implementation code is as follows:
class ScaffoldRoute extends StatefulWidget { @override State<StatefulWidget> createState() { return _ScaffoldRouteState(); } } class _ScaffoldRouteState extends State<ScaffoldRoute> { int _selectedIndex = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("App"), actions: [ IconButton( icon: Icon(Icons.share), onPressed: () => {}, ) ], ), drawer: Drawer( child: Text("drawer",style: TextStyle(fontSize: 40,color:Colors.blue),), ), bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "home page"), BottomNavigationBarItem(icon: Icon(Icons.list_alt), label: "list"), BottomNavigationBarItem(icon: Icon(Icons.person), label: "user") ], currentIndex: _selectedIndex, fixedColor: Colors.blue, onTap: _onItemTapped, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: _add, ), ); } void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } void _add() {} } Copy code
The components we use in the above code are:
- AppBar: a navigation bar skeleton
- MyDrawer: drawer menu
- BottomNavigationBar: bottom navigation bar
- FloatingActionButton: float button
AppBar
Appbar is a Material style navigation bar, through which you can set the title, navigation bar menu, navigation bottom tab, etc
AppBar({ Key key, this.leading, //The leftmost Widget on the navigation bar is usually a drawer menu button or a return button. this.automaticallyImplyLeading = true, //If leading is null, whether to automatically implement the default leading button this.title,// Page title this.actions, // Navigation bar right menu this.bottom, // The menu at the bottom of the navigation bar, usually a Tab button group this.elevation = 4.0, // Navigation bar shadow this.centerTitle, //Is the title centered this.backgroundColor, ... //See source code Notes for other attributes }) Copy code
If a drawer menu is added to Scaffold, by default, Scaffold will automatically set the leading of AppBar as the menu button (as shown in the screenshot above), and click it to open the drawer menu.
If you want to customize the menu icon, you can manually set the leading. For example:
AppBar( title: Text("App"), leading: Builder(builder: (context) { return IconButton( icon: Icon( Icons.dashboard, color: Colors.white, ), onPressed: () => {Scaffold.of(context).openDrawer()}, ); }), ........... ) Copy code
You can see that the menu on the left has been replaced successfully
The method to open the drawer is in Scaffold State through Scaffold Of() can get the State object of the nearest Scaffold component of the abdominal muscle
ToolBar
Next, create a TabBar component through the Bottom attribute in the AppBar, which can quickly generate the Tab menu,
class _ScaffoldRouteState extends State<ScaffoldRoute> with SingleTickerProviderStateMixin{ int _selectedIndex = 0; TabController _tabController; List tabs = ["Journalism", "history", "picture"]; @override void initState() { super.initState(); _tabController = TabController(length: tabs.length, vsync: this); } AppBar( title: Text("App"), bottom: TabBar( controller: _tabController, tabs: tabs.map((e) => Tab(text: e)).toList(), ), ........ ) } Copy code
The above code creates a TabController, which is used to listen to Tab menu switching, and then generates a menu bar through tabBar.
The tabs property of TabBar accepts a Widget array to represent each Tab submenu. We can customize the component style or directly use the Tab component as in the example
The Tab component has three optional parameters. In addition to knowing the text, you can also specify the Tab menu icon or customize the component style. The definitions are as follows:
Tab({ Key key, this.text, // menu text this.icon, // Menu icon this.child, // Custom component styles }) Copy code
Developers can customize according to the actual needs
TabBarView
Through TabBar, we can only generate a static menu, and the real Tab page has not been realized. Because the Tab menu and Tab page need to be switched, we need to monitor the switching of Tab menu through TabController, and then switch the Tab page. The code is as follows:
_tabController.addListener((){ switch(_tabController.index){ case 1: ...; case 2: ... ; } }); Copy code
If the Tab page can slide and switch, you also need to update the offset of the TabBar indicator during the sliding process. Obviously, this is very troublesome!
To this end, the Material library provides a TabBarView component, which can not only easily realize the Tab page, but also easily cooperate with the TabBar to realize the synchronization of switching and sliding States, as follows:
body: TabBarView( controller: _tabController, children: tabs.map((e) { return Container( alignment: Alignment.center, child: Text(e), ); }).toList(), ), Copy code
Now, whether you click Tab or slide the page, the Tab page will switch,
In the above example, the controllers of TabBar and TabBarView are the same. That's why TabBar and TabBarView realize menu switching and sliding state synchronization through one controller. The effects are as follows:
In addition, the Material component library also provides a pageview component, which has similar functions to TabBarView. Next, reorganize the above examples and use pageview to move the navigation bar below
class ScaffoldRoute extends StatefulWidget { @override State<StatefulWidget> createState() { return _ScaffoldRouteState(); } } class _ScaffoldRouteState extends State<ScaffoldRoute> with SingleTickerProviderStateMixin { int _selectedIndex = 0; PageController _pageController; TabController _tabController; List tabs = ["Journalism", "history", "picture"]; @override void initState() { super.initState(); _tabController = TabController(length: tabs.length, vsync: this); _pageController = PageController(); _tabController.addListener(() { print(_tabController.index); }); } @override Widget build(BuildContext context) { return Scaffold( body: PageView( controller: _pageController, children: [ home(), Container( alignment: Alignment.center, child: Text("list"), ), Container( alignment: Alignment.center, child: Text("user"), ) ], ), bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "home page"), BottomNavigationBarItem(icon: Icon(Icons.list_alt), label: "list"), BottomNavigationBarItem(icon: Icon(Icons.person), label: "user") ], currentIndex: _selectedIndex, fixedColor: Colors.blue, onTap: _onItemTapped, ), ); } Widget home() { return Scaffold( drawer: Drawer( child: Text( "drawer", style: TextStyle(fontSize: 40, color: Colors.blue), ), ), appBar: AppBar( title: Text("App"), leading: Builder(builder: (context) { return IconButton( icon: Icon( Icons.dashboard, color: Colors.white, ), onPressed: () => {Scaffold.of(context).openDrawer()}, ); }), actions: [ IconButton( icon: Icon(Icons.share), onPressed: () => {}, ) ], bottom: TabBar( controller: _tabController, tabs: tabs.map((e) => Text(e)).toList(), ), ), body: TabBarView( controller: _tabController, children: tabs.map((e) { return Container( alignment: Alignment.center, child: Text(e), ); }).toList(), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: _add, ), ); } void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); _pageController.jumpToPage(index); } void _add() {} } Copy code
The effect is as follows:
Drawer menu drawer
The drawer and endDrawer properties of Scaffold can accept a Widget as the left and right drawer menus respectively. The above example also uses the left drawer menu, which is modified as follows:
class DrawerTest extends StatelessWidget { @override Widget build(BuildContext context) { return Drawer( child: MediaQuery.removePadding( context: context, //Remove the top of the drawer menu and leave it blank by default removeTop: true, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 38), child: Row( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: ClipOval( child: Image.asset( "images/icon.png", width: 80, ), ), ), Text( "file", style: TextStyle(fontWeight: FontWeight.bold), ) ], ), ), Expanded( child: ListView( children: [ ListTile( leading: const Icon(Icons.add), title: const Text('Add account'), ), ListTile( leading: const Icon(Icons.settings), title: const Text('Manage accounts'), ), ], ), ) ], ), ), ); } } Copy code
drawer: Drawer( child: DrawerTest(), ), Copy code
The final effect is as follows:
Drawer menus usually take the drawer component as the root node. It is a menu panel in Materal style, mediaquery Removepadding can remove some of the default white space in the drawer
Bottom Tab navigation bar
We can set the bottom navigation through the BottomNavigationBar property of Scaffold. As in the above example, we implement the bottom navigation bar through the BottomNavigationBar and BottomNavigationBarItem provided by the Material component. The code is also very simple
But what to do if you want to achieve some special effects? Example:
bottomNavigationBar: BottomAppBar( color: Colors.white, shape: CircularNotchedRectangle(), child: Row( children: [ IconButton( icon: Icon(Icons.home), onPressed: () => _pageController.jumpToPage(0), ), SizedBox(), //The middle position is empty IconButton( icon: Icon(Icons.business), onPressed: () => _pageController.jumpToPage(2), ) ], //Divide the space of the bottom navigation bar equally mainAxisAlignment: MainAxisAlignment.spaceAround, ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () => _pageController.jumpToPage(1), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, Copy code
The effect is as follows:
You can see that there is no attribute of punching position in the above code. In fact, the punching position depends on the position of FloatingActionButton, which is floatingactionbuttonlocation Centerdocked, so the punching position is in the middle of the bottom navigation bar
The shape attribute of BottomAppBar determines the shape of the hole. Circular notchedrectangle implements a circular shape, which can also be customized;
Tailoring
Some clipping functions are provided in fluent for clipping components.
Crop Widget | effect |
---|---|
ClipOval | When the sub component is square, it is clipped as an inscribed circle; when it is rectangular, it is clipped as an inscribed ellipse in the Wie |
ClipRRect | Clipping subcomponents into rounded rectangles |
ClipRect | Clip subcomponents to the size of the rectangle actually occupied (overflow cutting) |
Chestnuts
Widget avatar = Image.asset("images/avatar.jpg", width: 80,); Copy code
Scaffold( appBar: AppBar( title: Text("Tailoring"), ), body: Center( child: Column( children: [ //Original drawing avatar, //Clip to circle ClipOval( child: avatar, ), //Clip to rounded rectangle ClipRRect( borderRadius: BorderRadius.circular(5), child: avatar, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ //Align: adjust the position of sub components, Align( alignment: Alignment.topLeft, widthFactor: .5,//Own = subcomponent x widthFactor child: avatar, ), Text( "Hello World", style: TextStyle(color: Colors.green), ) ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // Clip the overflow part ClipRect( child: Align( alignment: Alignment.topLeft, widthFactor: .5, child: avatar, ), ), Text( "Hello World", style: TextStyle(color: Colors.green), ) ], ) ], ), ), ); Copy code
It should be noted that after Align's widthFactor is 0.5, the actual width of the image is equal to 0.5 * 80, that is, half of the width
CustomClippear
If we want to clip a specific area of the sub component, for example, in the picture in the above example, what should we do if we only want to intercept the 40 * 30 upward range in the picture?
At this time, you can use CustomClipper to customize the clipping area, as shown below
1. Customize CustomClipper:
class MyClipper extends CustomClipper<Rect> { @override Rect getClip(Size size) { return Rect.fromLTWH(10, 15, 40, 30); } @override bool shouldReclip(covariant CustomClipper<dynamic> oldClipper) => false; } Copy code
- getClip The method to obtain the clipping area. The picture size is 80 * 80, and the area we return is rect From ltwh (10, 15, 40, 30), that is, the range of 40 * 30 pixels in the picture
- shouldReclip Whether to re clip. If the clipping area will not change in the application, false should be returned, so that re cutting will not be triggered to avoid unnecessary overhead. If there is a change (such as an animation in the clipping area), you should return true to perform the clipping again after the change
2. Perform clipping through ClipRect
DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: ClipRect( child: avatar, clipper: MyClipper(), ), ) Copy code
As shown above, it can be seen that the clipping is successful, but the size occupied by the picture is still 80 * 80. This is because the clipping is carried out in the drawing stage after the layout is completed, so it will not affect the size of the component. This transformation principle is similar.
Refer to the actual combat of Flutter