【Flutter实战】六大布局组件及半圆菜单案例

移动开发 作者: 2024-08-24 23:20:01
老孟导读:Flutter中布局组件有水平 / 垂直布局组件( Row 和 Column )、叠加布局组件( Stack 和 IndexedStack )、流式布局组件( Wrap )和 自定义布局组件

水平、垂直布局组件

Row(
  children: <Widget>[
    Container(
      height: 50,width: 100,color: Colors.red,),Container(
      height: 50,color: Colors.green,color: Colors.blue,],)
Column(
  mainAxisSize: MainAxisSize.min,children: <Widget>[
    Container(
      height: 50,)
Container(
  decoration: BoxDecoration(border: Border.all(color: Colors.black)),child: Row(
    children: <Widget>[
      Container(
        height: 50,Container(
        height: 50,)
  • spaceAround :第一个子控件距开始位置和最后一个子控件距结尾位置是其他子控件间距的一半。
  • spaceEvenly : 所有间距一样。
Container(
      decoration: BoxDecoration(border: Border.all(color: Colors.black)),child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[
          Container(
            height: 50,Container(
            height: 100,Container(
            height: 150,)
Container(
	decoration: BoxDecoration(border: Border.all(color: Colors.black)),child: Row(
		mainAxisSize: MainAxisSize.min,...
	)
)
Container(
  decoration: BoxDecoration(border: Border.all(color: Colors.black)),child: Row(
    textDirection: TextDirection.rtl,children: <Widget>[
      ...
    ],)
  • up :从底部开始,并垂直堆叠到顶部,对齐方式的 start 在底部,end 在顶部。
  • down: 与 up 相反。
Container(
  decoration: BoxDecoration(border: Border.all(color: Colors.black)),child: Row(
    crossAxisAlignment: CrossAxisAlignment.start,verticalDirection: VerticalDirection.up,children: <Widget>[
      Container(
        height: 50,Container(
        height: 100,Container(
        height: 150,)

叠加布局组件

Stack(
  children: <Widget>[
    Container(
      height: 200,width: 200,Container(
      height: 170,width: 170,Container(
      height: 140,width: 140,color: Colors.yellow,)
  ],)
Stack(
  fit: StackFit.expand,children: <Widget>[
    Container(
      height: 200,)
Stack(
  alignment: AlignmentDirectional.center,)
Stack(
  alignment: AlignmentDirectional.center,Positioned(
      left: 30,right: 40,bottom: 50,top: 60,child: Container(
        color: Colors.yellow,)
Stack(
  overflow: Overflow.visible,Positioned(
      left: 100,top: 100,height: 150,width: 150,child: Container(
        color: Colors.green,)
class IndexedStackDemo extends StatefulWidget {
  @override
  _IndexedStackDemoState createState() => _IndexedStackDemoState();
}

class _IndexedStackDemoState extends State<IndexedStackDemo> {
  int _index = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        SizedBox(height: 50,_buildIndexedStack(),SizedBox(height: 30,_buildRow(),);
  }

  _buildIndexedStack() {
    return IndexedStack(
      index: _index,children: <Widget>[
        Center(
          child: Container(
            height: 300,width: 300,alignment: Alignment.center,child: Icon(
              Icons.fastfood,size: 60,Center(
          child: Container(
            height: 300,child: Icon(
              Icons.cake,child: Icon(
              Icons.local_cafe,);
  }

  _buildRow() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[
        IconButton(
          icon: Icon(Icons.fastfood),onPressed: () {
            setState(() {
              _index = 0;
            });
          },IconButton(
          icon: Icon(Icons.cake),onPressed: () {
            setState(() {
              _index = 1;
            });
          },IconButton(
          icon: Icon(Icons.local_cafe),onPressed: () {
            setState(() {
              _index = 2;
            });
          },);
  }
}

流式布局组件

Wrap(
  children: List.generate(10,(i) {
    double w = 50.0 + 10 * i;
    return Container(
      color: Colors.primaries[i],height: 50,width: w,child: Text('$i'),);
  }),)
Wrap(
  direction: Axis.vertical,children: List.generate(4,)
Wrap(
	alignment: WrapAlignment.spaceBetween,...
)
  • spaceAround:第一个子控件距开始位置和最后一个子控件距结尾位置是其他子控件间距的一半。
  • spaceEvenly:所有间距一样。
Wrap(
	crossAxisAlignment: WrapCrossAlignment.center,...
)
Wrap(
  spacing: 5,runSpacing: 3,crossAxisAlignment: WrapCrossAlignment.center,children: List.generate(10,(i) {
    double w = 50.0 + 10 * i;
    double h = 50.0 + 5 * i;
    return Container(
      color: Colors.primaries[i],height: h,)
  • alignment :是主轴方向上对齐方式,作用于每一行。
  • runAlignment :是交叉轴方向上将每一行看作一个整体的对齐方式。
Wrap(
	spacing: 5,runSpacing: 2,...
)
Wrap(
	textDirection: TextDirection.rtl,...
)
Wrap(
	verticalDirection: VerticalDirection.up,...
)

自定义布局组件

Flow(
  delegate: SimpleFlowDelegate(),children: List.generate(5,(index) {
    return Container(
      height: 100,color: Colors.primaries[index % Colors.primaries.length],)
class SimpleFlowDelegate extends FlowDelegate {
  @override
  void paintChildren(FlowPaintingContext context) {
    for (int i = 0; i < context.childCount; ++i) {
      context.paintChild(i);
    }
  }

  @override
  bool shouldRepaint(SimpleFlowDelegate oldDelegate) {
    return false;
  }
}
class SimpleFlowDelegate extends FlowDelegate {
  @override
  void paintChildren(FlowPaintingContext context) {
    for (int i = 0; i < context.childCount; ++i) {
      context.paintChild(i,transform: Matrix4.translationValues(0,i*30.0,0));
    }
  }

  @override
  bool shouldRepaint(SimpleFlowDelegate oldDelegate) {
    return false;
  }
}

仿 掘金-我的效果

_buildCircleImg() {
  return Container(
    height: 60,width: 60,decoration: BoxDecoration(
        shape: BoxShape.circle,image: DecorationImage(image: AssetImage('assets/images/logo.png'))),);
}
_buildCenter() {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[
      Text('老孟Flutter',style: TextStyle(fontSize: 20),Text('Flutter、Android',style: TextStyle(color: Colors.grey),)
    ],);
}
Icon(Icons.arrow_forward_ios,color: Colors.grey,size: 14,
Container(
  color: Colors.grey.withOpacity(.5),child: Container(
    height: 100,color: Colors.white,child: Row(
      children: <Widget>[
        SizedBox(
          width: 15,_buildCircleImg(),SizedBox(
          width: 25,Expanded(
          child: _buildCenter(),Icon(Icons.arrow_forward_ios,SizedBox(
          width: 15,)

水平展开/收起菜单

class DemoFlowPopMenu extends StatefulWidget {
  @override
  _DemoFlowPopMenuState createState() => _DemoFlowPopMenuState();
}

class _DemoFlowPopMenuState extends State<DemoFlowPopMenu>
    with SingleTickerProviderStateMixin {
  //动画必须要with这个类
  AnimationController _ctrlAnimationPopMenu; //定义动画的变量
  IconData lastTapped = Icons.notifications;
  final List<IconData> menuItems = <IconData>[
    //菜单的icon
    Icons.home,Icons.new_releases,Icons.notifications,Icons.settings,Icons.menu,];

  void _updateMenu(IconData icon) {
    if (icon != Icons.menu) {
      setState(() => lastTapped = icon);
    } else {
      _ctrlAnimationPopMenu.status == AnimationStatus.completed
          ? _ctrlAnimationPopMenu.reverse() //展开和收拢的效果
          : _ctrlAnimationPopMenu.forward();
    }
  }

  @override
  void initState() {
    super.initState();
    _ctrlAnimationPopMenu = AnimationController(
      //必须初始化动画变量
      duration: const Duration(milliseconds: 250),//动画时长250毫秒
      vsync: this,//SingleTickerProviderStateMixin的作用
    );
  }

//生成Popmenu数据
  Widget flowMenuItem(IconData icon) {
    final double buttonDiameter =
        MediaQuery.of(context).size.width * 2 / (menuItems.length * 3);
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8.0),child: RawMaterialButton(
        fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue,splashColor: Colors.amber[100],shape: CircleBorder(),constraints: BoxConstraints.tight(Size(buttonDiameter,buttonDiameter)),onPressed: () {
          _updateMenu(icon);
        },child: Icon(icon,size: 30.0),);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Flow(
        delegate: FlowMenuDelegate(animation: _ctrlAnimationPopMenu),children: menuItems
            .map<Widget>((IconData icon) => flowMenuItem(icon))
            .toList(),);
  }
}
class FlowMenuDelegate extends FlowDelegate {
  FlowMenuDelegate({this.animation}) : super(repaint: animation);
  final Animation<double> animation;

  @override
  void paintChildren(FlowPaintingContext context) {
    double x = 50.0; //起始位置
    double y = 50.0; //横向展开,y不变
    for (int i = 0; i < context.childCount; ++i) {
      x = context.getChildSize(i).width * i * animation.value;
      context.paintChild(
        i,transform: Matrix4.translationValues(x,y,0),);
    }
  }

  @override
  bool shouldRepaint(FlowMenuDelegate oldDelegate) =>
      animation != oldDelegate.animation;
}

半圆菜单展开/收起

import 'dart:math';

import 'package:flutter/material.dart';

class DemoFlowMenu extends StatefulWidget {
  @override
  _DemoFlowMenuState createState() => _DemoFlowMenuState();
}

class _DemoFlowMenuState extends State<DemoFlowMenu>
    with TickerProviderStateMixin {
  //动画需要这个类来混合
  //动画变量,以及初始化和销毁
  AnimationController _ctrlAnimationCircle;

  @override
  void initState() {
    super.initState();
    _ctrlAnimationCircle = AnimationController(
        //初始化动画变量
        lowerBound: 0,upperBound: 80,duration: Duration(milliseconds: 300),vsync: this);
    _ctrlAnimationCircle.addListener(() => setState(() {}));
  }

  @override
  void dispose() {
    _ctrlAnimationCircle.dispose(); //销毁变量,释放资源
    super.dispose();
  }

  //生成Flow的数据
  List<Widget> _buildFlowChildren() {
    return List.generate(
        5,(index) => Container(
              child: Icon(
                index.isEven ? Icons.timer : Icons.ac_unit,));
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned.fill(
          child: Flow(
            delegate: FlowAnimatedCircle(_ctrlAnimationCircle.value),children: _buildFlowChildren(),Positioned.fill(
          child: IconButton(
            icon: Icon(Icons.menu),onPressed: () {
              setState(() {
                //点击后让动画可前行或回退
                _ctrlAnimationCircle.status == AnimationStatus.completed
                    ? _ctrlAnimationCircle.reverse()
                    : _ctrlAnimationCircle.forward();
              });
            },);
  }
}
class FlowAnimatedCircle extends FlowDelegate {
  final double radius; //绑定半径,让圆动起来
  FlowAnimatedCircle(this.radius);

  @override
  void paintChildren(FlowPaintingContext context) {
    if (radius == 0) {
      return;
    }
    double x = 0; //开始(0,0)在父组件的中心
    double y = 0;
    for (int i = 0; i < context.childCount; i++) {
      x = radius * cos(i * pi / (context.childCount - 1)); //根据数学得出坐标
      y = radius * sin(i * pi / (context.childCount - 1)); //根据数学得出坐标
      context.paintChild(i,-y,0));
    } //使用Matrix定位每个子组件
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) => true;
}
原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_68077.html