前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Flutter】Flutter 照片墙 ( Center 组件 | Wrap 组件 | ClipRRect 组件 | Stack 组件 | Positioned 组件 | 按钮组合组件 )

【Flutter】Flutter 照片墙 ( Center 组件 | Wrap 组件 | ClipRRect 组件 | Stack 组件 | Positioned 组件 | 按钮组合组件 )

作者头像
韩曙亮
发布2023-03-29 08:52:55
8.4K0
发布2023-03-29 08:52:55
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、Flutter 组件回顾


Flutter 与布局相关的组件 :

  • Container : 容器组件 ;
  • RenderObjectWidget : 布局渲染相关组件 ;
    • SingleChildRenderObjectWidget : 单节点布局组件 ;
      • Opacity : 常用于修改组件透明度 ;
      • ClipOval : 裁剪布局组件 , 可以将布局裁剪成圆形 ;
      • ClipRRect : 裁剪布局组件 , 可以将布局裁剪成方形 ;
      • PhysicalModel : 将布局显示成不同的形状 ;
      • Align : 布局设置组件 , 一般设置布局居中操作 ;
      • Padding : 设置内边距的组件 ;
      • SizeBox : 用于约束布局大小的组件 ;
      • FractionallySizedBox : 约束布局水平 / 垂直方向的平铺操作 ;
    • MultiChildRenderObjectWidget : 多节点布局组件 ;
      • Stack : 相当于帧布局 FrameLayout ;
      • Flex :
        • Column : 相当于线性布局 , 垂直方向布局 , 组件从上到下摆放 ;
        • Row : 相当于线性布局 , 水平方向布局 , 组件从左到右 ;
      • Wrap : 该组件与 Row 组件类似 , Wrap 组件可以换行 ;
      • Flow : 不常用 ;
  • ParentDataWidget :
    • Positioned : 用于固定组件位置的组件 ;
    • Flexible : 用于约束组件在父容器中展开大小的组件 ;

二、Center 组件


widthFactor ( 宽度因子 ) 和 heightFactor ( 高度因子 ) 用于控制该组件的宽高 , 类型为 double 浮点型 ;

  • 参数为空 : 如果参数为空 , 则填充整个布局 , 相当于 match_parent ;
  • 参数不为空 : 如果参数不为空 , 则对应的宽高是 宽度/高度因子
\times

子组件高度 ;

代码示例 : 下面的代码中 , Center 没有设置宽高因子 , 默认为空 , 则该 Center 组件自动填充父容器 , 内部有一个 Widget 子组件 , 注意是单个子组件 ;

代码语言:javascript
复制
Center(
  child: Wrap()
)

三、Wrap 组件


Column 组件是垂直方向的线性布局 , Row 组件是水平方向的线性布局 ,

Wrap 组件是在 Row 组件的基础上的水平线性布局 , 多了一个换行功能 , Wrap 组件可以有多行水平线性布局 ;

这是照片墙实现的主要组件 , Wrap 组件中由一组 Image 组件 List 集合作为子组件 ;

代码示例 :

代码语言:javascript
复制
// 可自动换行的水平线性布局
Wrap(
  // 设置水平边距
  spacing: 间距值 ( double 类型 ),
  // 设置垂直间距
  runSpacing: 间距值 ( double 类型 ),
  children: <Widget>[
  	设置若干子组件 
  ]
)

运行效果 : Center 组件填充整个屏幕 , Wrap 组件是 Center 的子组件 , 在中心显示 ;

在这里插入图片描述
在这里插入图片描述

参考博客 :

四、ClipRRect 组件


ClipRRect 组件是矩形切割组件 , 可以将组件切割成圆角矩形 ; borderRadius 属性用于设置圆角 , child 属性用于设置被切割的子组件 ;

代码示例 :

代码语言:javascript
复制
    // 设置底部的大图片
    ClipRRect(
      // 设置圆角半径 5 像素
      borderRadius: BorderRadius.circular(5),
      // 设置图片
      child: Image.file(file, width: 120, height: 90, fit: BoxFit.fill,),
    ),

运行效果 : 下图中的圆角矩形就是使用 ClipRRect 切割 Image 组件的效果 ;

在这里插入图片描述
在这里插入图片描述

参考博客 : 【Flutter】Flutter 布局组件 ( Opacity 组件 | ClipRRect 组件 | Padding 组件 ) 二、ClipRRect 组件

五、Stack 组件与 Positioned 组件


Stack 组件是帧布局组件 , 在其 children 字段设置一个 Widget 集合 ;

在 Stack 组件内部 , 可以使用 Positioned 组件指定某个子组件在 Stack 布局组件中的位置 ;

代码示例 :

代码语言:javascript
复制
// 帧布局
Stack(
  children: <Widget>[

    // 设置底部的大图片
    ClipRRect(
      // 设置圆角半径 5 像素
      borderRadius: BorderRadius.circular(5),
      // 设置图片
      child: Image.file(file, width: 120, height: 90, fit: BoxFit.fill,),
    ),

	// 使用 Positioned 组件在帧布局中定位子组件
    // 设置右上角的关闭按钮
    Positioned(
      // 距离右侧 5
      right: 5,
      // 距离顶部 5
      top: 5,
	  child: ,
	),
  ]
)

效果展示 : 整体是 Stack 帧布局 , 使用 ClipRRect 组件将 Image 组件切割成了圆角矩形 , Stack 组件内使用 Positioned 组件将关闭按钮 , 放置在了右上角 ;

在这里插入图片描述
在这里插入图片描述

参考博客 : 【Flutter】Flutter 布局组件 ( FractionallySizedBox 组件 | Stack 布局组件 | Positioned 组件 ) 二、Stack 布局组件

六、按钮组件组合


关闭按钮首先由按键功能 , 在最外围使用 GestureDetector 组件 , 监听器 onTap 点击事件 , 点击时删除对应的图片文件 , 并更新整体布局 ;

GestureDetector 组件的 child 子组件就是我们看到的关闭按钮 , 先使用 ClipOval 圆形切割组件切割出一个黑色圆形 , 在中间使用 Center 组件放置一个 Icon 白色图标 , 就组成了圆形的关闭按钮 ;

关闭按钮代码示例 :

代码语言:javascript
复制
// 手势检测器组件
GestureDetector(
  // 点击事件
  onTap: (){
    setState(() {
      // 从图片集合中移除该图片
      _images.remove(file);
    });
  },
  // 右上角的删除按钮
  child: ClipOval(
    child: Container(
      padding: EdgeInsets.all(3),
      // 背景装饰
      decoration: BoxDecoration(color: Colors.black),
      // 图标, 20 像素 , 白色 , 关闭按钮
      child: Icon(Icons.close, size: 20, color: Colors.white,),
    ),
  ),
),

运行效果 :

在这里插入图片描述
在这里插入图片描述

七、完整代码示例


完整代码示例 :

代码语言:javascript
复制
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: '拍照示例'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  /// 需要导入 dart:io 库
  /// import 'dart:io';
  File _image;

  /// 存放获取的图片集合, 初始化时为空
  List<File> _images = [];

  // 图片获取引擎
  final picker = ImagePicker();

  /// 获取摄像头图像的方法
  Future getImageFromCamera() async {
    /// 菜单按钮消失
    Navigator.pop(context);

    /// 需要导入 image_picker.dart 包
    /// import 'package:image_picker/image_picker.dart';
    final pickedFile =
    await picker.getImage(source: ImageSource.camera);

    setState(() {
      if (pickedFile != null) {
        //_image = File(pickedFile.path);
        /// 添加到图片文件集合中
        _images.add(File(pickedFile.path));
      } else {
        print('No image selected.');
      }
    });
  }

  /// 获取相册中的图像
  Future getImageFromGallery() async {
    /// 菜单按钮消失
    Navigator.pop(context);

    /// 需要导入 image_picker.dart 包
    /// import 'package:image_picker/image_picker.dart';
    final pickedFile =
    await picker.getImage(source: ImageSource.gallery);

    setState(() {
      if (pickedFile != null) {
        //_image = File(pickedFile.path);
        /// 添加到图片文件集合中
        _images.add(File(pickedFile.path));
      } else {
        print('No image selected.');
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Wrap(
          spacing: 5,
          runSpacing: 5,
          children:


          // 遍历 从相册选择的照片 或 相机拍摄的照片
          _images.map((file){
            // 每个照片都生成一个帧布局
            // 照片填充整个布局, 右上角放置一个关闭按钮
            return Stack(
              children: <Widget>[

                // 设置底部的大图片
                ClipRRect(
                  // 设置圆角半径 5 像素
                  borderRadius: BorderRadius.circular(5),
                  // 设置图片
                  child: Image.file(file, width: 120, height: 90, fit: BoxFit.fill,),
                ),

                // 设置右上角的关闭按钮
                Positioned(
                  // 距离右侧 5
                  right: 5,
                  // 距离顶部 5
                  top: 5,

                  // 手势检测器组件
                  child: GestureDetector(
                    // 点击事件
                    onTap: (){
                      setState(() {
                        // 从图片集合中移除该图片
                        _images.remove(file);
                      });
                    },

                    // 右上角的删除按钮
                    child: ClipOval(
                      child: Container(
                        padding: EdgeInsets.all(3),

                        // 背景装饰
                        decoration: BoxDecoration(color: Colors.black),

                        // 图标, 20 像素 , 白色 , 关闭按钮
                        child: Icon(Icons.close, size: 20, color: Colors.white,),
                      ),
                    ),

                  ),
                )

              ],
            );

            /// 注意这里要转为 List 类型 , <Widget>[]
          }).toList()


          ,

        )
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          /// 浮动按钮点击事件
          /// 点击浮动按钮 , 弹出一个菜单
          /// 菜单有两个按钮 , 分别是 拍照 / 选择图片
          showModalBottomSheet(
              context: context,
              builder: (context) {
                return Container(
                  // 设置该弹出的组件高度 200 像素
                  height: 200,
                  child: Column(
                    children: <Widget>[
                      // 拍照按钮
                      GestureDetector(
                        child: ListTile(
                          // 相机图标
                          leading: Icon(Icons.camera_alt),
                          title: Text("拍照"),
                          /// 按钮点击事件
                          onTap: (){
                            // 调用 getImage 方法 , 调出相机拍照
                            getImageFromCamera();
                          },
                        ),
                      ),

                      // 相册按钮
                      GestureDetector(
                        child: ListTile(
                          // 相册图标
                          leading: Icon(Icons.photo_library_outlined),
                          title: Text("相册"),
                          /// 按钮点击事件
                          onTap: (){
                            // 调用 getImageFromGallery 方法 , 调出相册
                            getImageFromGallery();
                          },
                        ),
                      ),

                    ],
                  ),
                );
              });
        },
        tooltip: 'Pick Image',
        child: Icon(Icons.add_a_photo),
      ),
    );
  }

  _generateImageWidgets() {


  }
}

运行效果 : 拍照获取第一个图片 , 从相册中选择第二章图片 , 然后删除第一张图片 ;

在这里插入图片描述
在这里插入图片描述

八、相关资源


参考资料 :

博客源码下载 :

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-03-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、Flutter 组件回顾
  • 二、Center 组件
  • 三、Wrap 组件
  • 四、ClipRRect 组件
  • 五、Stack 组件与 Positioned 组件
  • 六、按钮组件组合
  • 七、完整代码示例
  • 八、相关资源
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档