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 components
  • LimitedBox: Specifies the maximum width and height
  • FractionallySizedBoxYou 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 passedwidthheightProperty, can also be specified byconstraintsTo define; If both exist, thenwidthheightIs 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