Padding
Padding can be used to add Padding (white space) to child nodes, similar to the effect of margins, as defined below:
Padding({
...
EdgeInsetsGeometry padding,
Widget child,
})
Copy the code
EdgeInsetsGeometry is an abstract class. In development, we generally use the EdgeInsets class, which is a subclass of EdgeInsetsGeometry and defines the method for setting the fill
EdgeInsets
- FromLTRB (double left, double top, double right, double bottom) : Specifies the fill in four directions
- All (double value) : all directions are filled with the same value
- Only ({left, top, right,bottom}) : you can set the padding in a specific direction. You can specify multiple directions at the same time
- Symmetric ({vertical, horizontal}) : Used to set the symmetric direction
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 the code
Size limit class containers
ConstrainedBox, SizedBox, UnconstrainedBox, AspectRatio, etc.
ConstrainedBox
ConstrainedBox is used to add additional constraints to child components. For example, set the minimum height
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 the code
As you can see, although the Container is set to 10, it ends up showing 50 pixels. This is the minimum height of ConstrainedBox in 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 the code
BoxConstraints defines a number of convenient constructors for quickly generating BoxConstraints
- Tigth (size) : Generates a limit for a given size
- Expand () : You can generate a BoxConstraints as large as possible
- There are a few others, as shown here:
SizedBox
Used to give a fixed width and height to child elements, for example:
Widget getSizedBox() {
return SizedBox(width: 50, height: 50, child: getRedBackground());
}
Widget getRedBackground() {
return DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
);
}
Copy the code
In fact, SizedBox is just a custom of ConstrainedBox
@override
RenderConstrainedBox createRenderObject(BuildContext context) {
return RenderConstrainedBox(
additionalConstraints: _additionalConstraints,
);
}
BoxConstraints get _additionalConstraints {
return BoxConstraints.tightFor(width: width, height: height);
}
Copy the code
RenderConstrainedBox both Sized and ConstrainedBox use RenderConstrainedBox, and their createRenderObject methods return a RenderConstrainedBox
Multiple restrictions
If a component has more than one parent ConstrainedBox, which one ultimately takes effect, for example:
Widget getConstrainedBoxS() {
return ConstrainedBox(
constraints: BoxConstraints(minWidth: 90, minHeight: 50),
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 50, minHeight: 90),
child: getRedBackground(),
),
);
}
Copy the code
According to the width and height known above, for minWidth and minHeight, the corresponding values of the parent and child are larger. In fact, this is the only way to ensure that parent restrictions do not conflict with child restrictions
UnconstrainedBox
This component does not impose any restrictions on the child components, it allows the child components to be drawn to their own size. In general, we rarely use this component, but it may be helpful when removing multiple restrictions, as follows:
Widget getUnConstrainedBox() {
return ConstrainedBox(
constraints: BoxConstraints(minWidth: 90, minHeight: 50),
// Remove the parent limit
child: UnconstrainedBox(
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 50, minHeight: 90),
child: getRedBackground(),
),
),
);
}
Copy the code
You can see that the parent limit has been removed, and the final display is 50×50.
Note, however, that this limit can actually be removed. There is still white space left and right, which means that the parent limit exists, but it does not affect the size of the child getRedBackground(), but it still takes up space.
So is there any way to completely remove restrictions? The answer is no! So be careful if you want to restrict subcomponents in development, because once you restrict the specified conditions, it can be very difficult for subcomponents to customize their sizes.
In practice, when we find that we have used SizedBox or ConstrainedBox to give the width and height of the stator element and still have no effect, it is almost certain that a parent element has set a limit!
For example, in the menu on the right of AppBar in the Material component, we use SizedBox to specify the loading button size as follows:
AppBar(
title: Text("Box"),
actions: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(Colors.white70),
),
)
],
),
Copy the code
As can be seen, loading did not change due to the size of the setting. This is because the restriction condition of action has been specified in Appbar, so we need to remove the restriction according to the size of laoding defined 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 the code
Padding is used to create an inside margin to prevent Padding on the right edge of the screen
Other containers restrict classes
In addition to the containers described above, there are a few other size-restricted class containers, such as:
AspectRatio
: You can know the aspect ratio of child componentsLimitedBox
: Specifies the maximum width and heightFractionallySizedBox
You can set the child component width based on the parent container width to height ratio,
Because these are relatively simple to use, use when you can understand
Decorative container
DecoratedBox
A DecoratedBox can be used to paint decorations, such as backgrounds, borders, gradients, etc., before (or after) its children:
const DecoratedBox({
Decoration decoration,
DecorationPosition position = DecorationPosition.background,
Widget child
})
Copy the code
- Decoration is an abstract class that defines an interface called createBoxPainter(). The subclass’s main job is to implement it to create a brush that will be used to paint decorations.
- Position: This property determines where to draw Decoration. It accepts an enumeration of DecorationPostition, which has two types:
- Background: Draws after the child component
- Foreground: Draws over the child component, ie foreground
BoxDecoration
We usually use the BoxDecoration class directly, which is a subclass of Decoration and implements the drawing of commonly used Decoration elements
BoxDecoration({
Color color, / / color
DecorationImage image,/ / picture
BoxBorder border, / / frame
BorderRadiusGeometry borderRadius, / / the rounded
List<BoxShadow> boxShadow, // Multiple shadows can be specified
Gradient gradient, / / the gradient
BlendMode backgroundBlendMode, // Background blending mode
BoxShape shape = BoxShape.rectangle, / / shape
})
Copy the code
Below implement a button with a shaded background gradient
class DecoratedTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("DecoratedTest"),
),
body: Container(
child: DecoratedBox(
// foreground = foreground
position: DecorationPosition.background,
decoration: BoxDecoration(
// Background gradient
gradient:
LinearGradient(colors: [Colors.red, Colors.orange[700]]),
/ / the rounded
borderRadius: BorderRadius.circular(10),
/ / the 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 the code
Use the DecoratedBox to decorate child elements, such as gradients, rounded corners, and shadows in the above example, which looks like this:
The DecoratedBox class works like shap on Android, adding styles to controls.
Transform the Transform
Transform can apply matrix transformations to its children as they are drawn to achieve special effects.
Matrix4 is a 4D matrix through which we can implement various rectangular operations, examples:
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 along the Y-axis by 0.3 radians
child: Container(
padding: const EdgeInsets.all(8),
color: Colors.deepOrange,
child: const Text('Apartment for rent! '),),); }}Copy the code
translation
Transform.translate takes an offset argument that can be translated a specified distance along the X and y axes to the child components when drawing
/ / translation
Widget getTranslate() {
return DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
child: Transform.translate(
offset: Offset(10.10),
child: Text("hello world"),),); }Copy the code
rotating
Transform.rotate Enables rotation changes to child 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 the code
Note that Math requires the package guide
import 'dart:math' as math;
Copy the code
The zoom
Transform.scale can be used to shrink or enlarge child components
Widget getScale() {
return DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
child: Transform.scale(
scale: 1.5,
child: Text("hello world"),),); }Copy the code
Pay attention to
The Transform changes during the draw phase, not layout phase, so whatever changes are made to the child component, the size of the space it takes up and its position on the screen remain the same, because these changes are determined during the layout phase. For example:
Widget getTest() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
getScale(),
Text(
"Hello",
style: TextStyle(color: Colors.green, fontSize: 18), a)],); }Copy the code
Since the Text in getScale takes up the same space as the red part, the second Text will be next to the red part and will eventually overlap
Since matrix transformation only works in the drawing stage, in some scenarios, when UI needs to be changed, visual UI changes can be achieved through matrix transformation instead of re-building the process, which will save the cost of layout and thus achieve better performance. For example, Flow components, Internally, a matrix Transform is used to update the UI. In addition, the Animation components of Flutter also make extensive use of Transform to improve performance
RotatedBox
RotatedBox and Transform.rotate are similar in functionality with one difference: RotatedBox changes in the Layout phase, affecting the position and size of child components
Modify the chestnuts above:
Widget getTest() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
getRotate(),
Text(
"Hello",
style: TextStyle(color: Colors.green, fontSize: 18), a)],); } Widget getRotate() {return DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
child: RotatedBox(
quarterTurns: 1.// Rotate 90 degrees (1/4)
child: Text("hello world"),),); }Copy the code
Since RotatedBox is applied to the layout stage, the subcomponents will be rotated 90 degrees (instead of drawing content), and decoration will be applied to the actual space occupied by the subcomponents, so the final effect is as shown in the figure above
Container
We’ve used the Container component many times before. A Container is a composite class Container that doesn’t correspond to a specific RenderObject, It is a multifunctional container composed of DecoratedBox, ConstrainedBox, Transform, Padding, Align, and more
The Container component allows you to decorate, change, and restrict all at the same time.
Container({
this.alignment,
this.padding, // Fill white inside the container, belonging to the range of decoration
Color color, / / the background color
Decoration decoration, // Background decoration
Decoration foregroundDecoration, // Foreground decoration
double width,// Width of the container
double height, // Height of the container
BoxConstraints constraints, // Constraints on the size of the container
this.margin,// Fill the outside of the container, not belonging to the scope of decoration
this.transform, / / transformation
this.child,
})
Copy the code
Most of the attributes of containers have been covered, but there are two things that need to be said:
- The size of the container can be passed
width
,height
Property, can also be specified byconstraints
To define; If both exist, thenwidth
和height
Is preferred. Inside the Container, it actually generates a constraints based on width and height - Color and decoration are mutually exclusive. If both are specified, an error will be reported. In fact, when color is specified, an decoration is automatically created inside 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 the code
As can be seen from the source code, Contrainer is a combination of multiple components described earlier. In Flutter, the Container component is also an example of combination over inheritance
The Padding and Margin
Container(
margin: EdgeInsets.all(20.0), // Fill the container
color: Colors.orange,
child: Text("Hello world!"),
),
Container(
padding: EdgeInsets.all(20.0), // Fill the container with white
color: Colors.orange,
child: Text("Hello world!"),),Copy the code
The effect is similar to Android’s padding/margin, where padding is the inside margin and margin is the outside margin
In fact, margin and padding are implemented using 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 the code
It’s just a Padding on the top layer
Scaffold, TabBar, bottom navigation
A complete routing page might have navigation bars, Drawer menus, bottom Tab navigation menus, etc. If every routing page had to be done manually, it would be boring and cumbersome.
Fortunately, the Flutter Material component library provides some ready-made components to reduce our development tasks
Scaffold
Scaffold is a skeleton of routing pages that can be easily assembled into a full page
We implement a page that contains
1, navigation bar, navigation bar button
2. Drawer menu
3. Bottom navigation
4. Hover button in 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: "A 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 the code
The components we used in the above code are:
- AppBar: A navigation bar skeleton
- MyDrawer: Drawer menu
- BottomNavigationBar: BottomNavigationBar
- FloatingActionButton: Float button
AppBar
Appbar is a Material style navigation bar, through which you can set the title, navigation bar menu, navigation bottom TAB and so on
AppBar({
Key key,
this.leading, // The left-most Widget in the navigation bar is usually a drawer menu button or a back button.
this.automaticallyImplyLeading = true.// If leading is null, the default leading button is automatically implemented
this.title,// Page title
this.actions, // Menu on the right of navigation bar
this.bottom, // The bottom menu of the navigation bar, usually a Tab button group
this.elevation = 4.0.// Navigation bar shadow
this.centerTitle, // Whether the title is centered
this.backgroundColor,
... // See source code comments for additional attributes
})
Copy the code
If Scaffold is added with a drawer menu, by default that Scaffold automatically sets AppBar’s Leading to the menu button (as shown in the screenshot above), which opens the drawer menu.
To customize menu ICONS, manually set Leading. Such as:
AppBar(
title: Text("App"),
leading: Builder(builder: (context) {
returnIconButton( icon: Icon( Icons.dashboard, color: Colors.white, ), onPressed: () => {Scaffold.of(context).openDrawer()}, ); }),...)Copy the code
You can see that the menu on the left has been replaced successfully
ScaffoldState is used to obtain the ScaffoldState object of the scaffold.of () Scaffold component
ToolBar
Next, create a TabBar component in AppBar using the Bottom property, which can quickly generate Tab menus.
class _ScaffoldRouteState extends State<ScaffoldRoute> with SingleTickerProviderStateMixin{
int _selectedIndex = 0;
TabController _tabController;
List tabs = ["News"."History"."Image"];
@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 the code
The above code creates a TabController that listens for Tab menu switches and then generates a menu bar from the tabBar.
The Tabs property of TabBar accepts an array of widgets representing each Tab submenu. You can either customize the component style or use the Tab component directly, 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 as follows:
Tab({
Key key,
this.text, // Menu text
this.icon, // Menu icon
this.child, // Customize component styles
})
Copy the code
Developers can customize according to actual requirements
TabBarView
With TabBar we can only generate a static menu; real Tab pages are not yet implemented. Since the switch between Tab menu and Tab page needs to be passed, we need to monitor the switch of Tab menu through TabController, and then switch Tab page. The code is as follows:
_tabController.addListener((){
switch(_tabController.index){
case 1: ...;
case 2:... ; }});Copy the code
If the Tab page can be swiped, the offset of the TabBar indicator needs to be updated as the Tab page is swiped, which can be very troublesome!
For this purpose, the Material library provides a TabBarView component that not only makes it easy to implement Tab pages, but also makes it very easy to synchronize state by switching and sliding with TabBar, as follows:
body: TabBarView(
controller: _tabController,
children: tabs.map((e) {
return Container(
alignment: Alignment.center,
child: Text(e),
);
}).toList(),
),
Copy the code
Now, whether YOU hit Tab or swipe the page, the Tab will switch,
In the example above, TabBar and TabBarView both have the same controller, which is exactly what TabBar and TabBarView do with a single controller. The effect is as follows:
In addition, the Material component library also provides a PageView component, which is similar to the TabBarView function. Let’s rework the above example and use PageView to animate 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 = ["News"."History"."Image"];
@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("A 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: "A 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 the code
The effect is as follows:
Menu Drawer
The Scaffold’s drawer and endDrawer properties accept a Widget for the left and right drawer menus, respectively. The left drawer menu is also used in the above example. Here is a change:
class DrawerTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: MediaQuery.removePadding(
context: context,
// Remove top drawer menu, default blank
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(
"Document",
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 the code
drawer: Drawer(
child: DrawerTest(),
),
Copy the code
The final effect is as follows:
Drawer menus typically use Drawer components as root nodes, which are limited to materal-style menu panels. Mediaquery. removePadding can remove some of the default whitespaces of drawers
Bottom Tab navigation bar
Bottom navigation can be set using the BottomNavigationBar property of the Scaffold as shown in the example above, We implement the BottomNavigationBar with the BottomNavigationBar and BottomNavigationBarItem provided by the Material component, and the code is very simple
But what to do if you want to achieve something special, for 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 the code
The effect is as follows:
As you can see, the code above without punch position attributes, in fact, punch position depends on the location, FloatingActionButton. The location of the above for FloatingActionButtonLocation centerDocked, So the location of the punch is right in the middle of the bottom navigation bar
BottomAppBar shape attribute determines the shape of the hole, CircularNotchedRectangle achieved a circular shape, we can also customize;
clipping
Clipping functions are provided in Flutter for clipping components.
Cut out the Widget | role |
---|---|
ClipOval | If the subcomponent is square, it is cut to circle inside, and if it is rectangle, it is cut to oval inside |
ClipRRect | Crop the child components as rounded rectangles |
ClipRect | Trim subcomponents to the actual occupied rectangle size (trim overflow parts) |
chestnuts
Widget avatar = Image.asset("images/avatar.jpg", width: 80,);
Copy the code
Scaffold(
appBar: AppBar(
title: Text("Cut"),
),
body: Center(
child: Column(
children: [
/ / the original image
avatar,
// Crop to a circle
ClipOval(
child: avatar,
),
// Crop to rounded rectangle
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: avatar,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//Align: adjust the position of child components,
Align(
alignment: Alignment.topLeft,
widthFactor: . 5.// own = child component x widthFactor
child: avatar,
),
Text(
"Hello World",
style: TextStyle(color: Colors.green),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Trim the overflow
ClipRect(
child: Align(
alignment: Alignment.topLeft,
widthFactor: . 5,
child: avatar,
),
),
Text(
"Hello World",
style: TextStyle(color: Colors.green),
)
],
)
],
),
),
);
Copy the code
Note that after the widthFactor of Align is 0.5, the actual width of the image is equal to 0.5 *80, which is half the width
CustomClippear
If we want to crop a specific area of the child component, for example, in the example above, what if we want to harvest only the 40 * 30 upward range of the image?
At this point you can customize the clipping area using CustomClipper, as shown below
1, Custom 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 the code
-
getClip
To get the clipping area, the image size is 80*80 and we return rect.fromltwh (10, 15, 40, 30), which is the 40 * 30 pixel range in the image
-
shouldReclip
Should return false if the clipping area never changes in the application so that reclipping is not triggered and unnecessary overhead is avoided. If there is a change (such as performing an animation in the clipping area), then the change should return true to re-crop
2. Perform clipping through ClipRect
DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
child: ClipRect(
child: avatar,
clipper: MyClipper(),
),
)
Copy the code
As shown above, we can see that the clipping is successful, but the size of the image is still 80 * 80. This is because clipping is carried out in the drawing stage after the layout is completed, so the size of the component will not be affected. This Transform principle is similar.
Refer to self Flutter actual combat