Flutter那些事-展示型组件篇

120 阅读10分钟

Text

在 Flutter 中,Text 组件是负责显示文本的核心部件,几乎出现在任何需要展示文字的地方。它非常灵活,既可以显示简单的单一样式文本,也能通过 Text.rich 构建复杂的富文本效果。

1. 核心用法与基本参数

使用 Text 最基本的方式就是创建一个 Text 实例,并传入要显示的字符串。但通过设置不同的参数,可以控制其布局和截断方式。

  • textAlign (文本对齐) :控制文本在 Text 组件内的水平对齐方式,例如 TextAlign.center(居中)、TextAlign.left(左对齐)。需要注意的是,只有当 Text 组件的宽度大于文本实际内容宽度时,对齐设置才会生效。

  • maxLines (最大行数) :限制文本最多显示的行数。默认情况下,文本会自动换行。设置此参数后,超出部分的行将不会被显示-1

  • overflow (溢出处理) :当文本内容超出 maxLines 限制或父容器约束时,如何处理溢出。常用的有:

    • TextOverflow.ellipsis:用省略号“...”表示被截断的文本。
    • TextOverflow.clip:直接裁剪掉溢出的文本(默认行为)。
    • TextOverflow.fade:将溢出的文本边缘渐隐处理。
  • softWrap (是否软换行) :决定文本是否在换行符处自动换行。如果设为 false,文本将只占一行,可能导致水平方向溢出。

  • textScaleFactor (文本缩放因子) :一个快速放大或缩小文本的因子。例如,设为 1.5 会使所有文本放大50%。不过,官方现在更推荐使用 textScaler 来获得更精细的控制。

示例:

import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(MainPage());
}

class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Text组件"),
        ),
        body: Container(
          // 文本居中方式
          alignment: Alignment.center,
          width: double.infinity,
          height: double.infinity,
          color: Colors.amber,
          child: Text.rich(TextSpan(
            text: "Hello",
            children: [
              TextSpan(
                text: "Flutter",style: TextStyle(color: Colors.green)
              ),
              TextSpan(text:"!")
            ],
            style: TextStyle(
              // 字体颜色
              color: Colors.red,
              // 字体大小
              fontSize: 40,
              // 字重
              fontWeight: FontWeight.bold

            )
          )),
          // 当字体显示数量超过对应行数时,其余文本将进行隐藏,并截断文本用...显示。
          // child: Text("今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,今天天气很冷,",
          //   style: TextStyle(
          //     color: Colors.blue,
          //     fontSize: 30
          //   ),
          //   maxLines: 2,
          //   // 溢出隐藏 
          //   overflow: TextOverflow.ellipsis,
          // ),
          // 基础Text组件
          // child:Text("Hello Flutter!",style: TextStyle(
          //   fontSize: 30,
          //   color: Colors.blue,
          //   fontStyle: FontStyle.italic,
          //   fontWeight: FontWeight.w600,
          //   decoration: TextDecoration.underline,
          //   decorationColor: Colors.red
          // ),),
        )
      ),
    );
  }
}

2. 样式定制 (TextStyle)

Text 组件的样式主要通过 style 属性来设置,其值是一个 TextStyle 对象。TextStyle 提供了极其丰富的参数来定义文本的外观-5-10

  • 基础样式:如 color(颜色)、fontSize(字号)、fontWeight(字重,如粗体)、fontStyle(样式,如斜体)。

  • 间距与行高

    • letterSpacing 和 wordSpacing 控制字符和单词的间距。
    • height 用于设置行高,它的值是 fontSize 的倍数。例如 height: 1.5 且 fontSize: 20.0,则最终行高为 30.0 逻辑像素。
  • 装饰效果

    • decoration:添加下划线 (TextDecoration.underline)、删除线 (TextDecoration.lineThrough) 等。
    • decorationColor 和 decorationStyle 分别设置装饰的颜色和样式(如虚线、波浪线)。
  • 高级效果:通过 background 和 foreground 属性,可以使用 Paint 对象实现更复杂的效果,比如给文本添加背景色,或使用渐变色、描边等。

示例:

Text(
  'Hello World',
  style: TextStyle(
    // 字体大小
    fontSize: 24.0,
    // 字体颜色
    color: Colors.blue,
    
    fontWeight: FontWeight.bold,
    // 装饰央视
    decoration: TextDecoration.underline,
    // 字体颜色
    decorationColor: Colors.red,
    // 字符
    decorationStyle: TextDecorationStyle.wavy,
    // 字符间距
    letterSpacing: 2.0,
  ),
)

3. 富文本 (TextSpan 与 Text.rich)

如果一段文本中需要包含多种样式(例如一句话里部分文字是蓝色且可点击),就不能使用普通的 Text 了。这时需要用到 TextSpan 和 Text.rich 构造函数。

TextSpan 代表一个具有独立样式的文本“片段”。多个 TextSpan 可以组合在一起,形成复杂的富文本结构。

  • text:显示的文本。
  • style:显示文本的样式。
  • children:一个 TextSpan 列表,用于嵌套更多的片段。
  • recognizer:为这个片段添加手势识别,比如点击事件。

示例:
以下代码创建了一个带有可点击链接的富文本。

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

Text.rich(
  TextSpan(
    text: '访问 ',
    children: <TextSpan>[
      TextSpan(
        text: 'Flutter 官网',
        style: const TextStyle(color: Colors.blue),
        recognizer: TapGestureRecognizer()
          ..onTap = () {
            // 处理点击事件,例如打开网页
            print('链接被点击了');
          },
      ),
      TextSpan(text: ' 了解更多。'),
    ],
  ),
)

4. 全局样式管理 (DefaultTextStyle)

在一个页面的多个地方使用相同的文本样式时,如果每个 Text 都单独设置 style,代码会显得冗余。Flutter 提供了 DefaultTextStyle 组件来管理全局或局部的默认文本样式。

DefaultTextStyle 可以包裹一个组件子树,并为其设置默认的 style 和 textAlign。该子树中所有未显式指定样式的 Text 组件,都会自动继承这个默认样式。

示例:

dart

复制下载

// 默认文本样式
DefaultTextStyle(
  style: const TextStyle(
    color: Colors.red,
    fontSize: 20.0,
  ),
  // 文本居中方式
  textAlign: TextAlign.center,
  child: Column(
    children: const [
      Text('这段文字是红色的20号字并居中'), // 继承样式
      Text('我也是同样的样式'),
      Text(
        '但我有不同样式',
        style: TextStyle( // 局部覆盖
          inherit: false, // 可选择不继承默认样式
          color: Colors.grey,
        ),
      ),
    ],
  ),
)

通过设置 inherit: false,子 Text 可以选择完全不继承父级的默认样式。

5. 使用自定义字体

要在应用中使用自定义字体(如非系统默认的字体文件),需要完成两个步骤:

  1. 在 pubspec.yaml 中声明:将字体文件(通常放在 fonts 或 assets 目录下)的路径和样式在配置文件中声明。

    flutter:
      fonts:
        - family: MyCustomFont  # 字体的家族名称
          fonts:
            - asset: fonts/MyCustomFont-Regular.ttf
            - asset: fonts/MyCustomFont-Bold.ttf
              weight: 700  # 指定该文件对应的字重
    
  2. 在 TextStyle 中使用:通过 fontFamily 属性引用你在 pubspec.yaml 中定义的字体家族名称。

    Text(
      '这是自定义字体',
      style: TextStyle(
        fontFamily: 'MyCustomFont',
        fontSize: 30.0,
      ),
    )
    

Image

在 Flutter 中,Image 组件是用于显示图片的核心部件。它能够从不同的来源加载图片,并提供丰富的参数来控制图片的显示尺寸、缩放模式、颜色混合等效果。

下面我们来详细解析 Image 组件的图片来源、核心参数、缓存机制以及常见问题。

1. 图片的来源

Flutter 提供了多种构造函数,用于从不同的数据源加载图片:

构造函数描述使用场景
Image.asset从应用程序的资源(pubspec.yaml 中声明的文件)中加载图片。加载应用内置的图标、背景图、本地静态图片。
Image.network从网络 URL 加载图片。加载用户头像、商品图片等来自服务器的动态图片。
Image.file从本地文件系统中加载图片(需要相应的权限)。加载用户从相册选择的图片,或应用下载到本地的图片。
Image.memory从内存中的 Uint8List 数据加载图片。加载动态生成的图片,或已经从网络/文件读取到内存中的图片数据。
Image通用构造函数,需要配合 ImageProvider 使用。当你需要自定义 ImageProvider 时使用。

示例:

import 'package:flutter/material.dart';
void main(List<String> args) {
  runApp(MainPage());
}
class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Text组件"),
        ),
        body: Container(
          alignment: Alignment.center,
          width: double.infinity,
          height: double.infinity,
          decoration: BoxDecoration(color: Colors.amber),
          // 从应用程序中加载资源
          child: Image.asset("lib/images/github.jpg",
            width: 100,
            height: 100,
            fit: BoxFit.cover,
            // fit:BoxFit.contain
            // fit:BoxFit.fill,
          ),
        )
        ,
      ),
    );
  }
}
import 'package:flutter/material.dart';
void main(List<String> args) {
  runApp(MainPage());
}
class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Text组件"),
        ),
        body: Container(
          alignment: Alignment.center,
          width: double.infinity,
          height: double.infinity,
          decoration: BoxDecoration(color: Colors.amber),
          // 从网络中加载图片资源
          child: Image.network("https://vmmp.qpic.cn/vmedia/xh/mulpic/mzc00200m2a6lpy_1280_720_1771402161898/0?imageView2/2/w/700",
            width: 400,
            height: 300,
            fit: BoxFit.cover,
          ),
          
        )
        ,
      ),
    );
  }
}


2. 核心参数与用法

Image 组件提供了丰富的参数来精确控制图片的显示效果。

2.1 控制尺寸

  • width & height:显式设置图片的宽度和高度。如果不设置,Image 会尝试根据父容器约束和图片原始尺寸来决定自己的大小。

  • boxFit:这是最重要的参数之一,用于指定图片的缩放模式,决定图片如何适应 Image 组件分配到的空间。它接收一个 BoxFit 枚举值:

    • BoxFit.contain:保持图片原始宽高比,使图片完整地显示在组件内。可能导致组件区域留有空白。
    • BoxFit.cover:保持图片原始宽高比,使图片完全覆盖组件区域。图片可能会被裁剪。
    • BoxFit.fill:不保持宽高比,拉伸图片以填满组件区域。图片可能会失真。
    • BoxFit.fitWidth:保持宽高比,将图片的宽度拉伸至与组件宽度一致,高度自动调整。
    • BoxFit.fitHeight:保持宽高比,将图片的高度拉伸至与组件高度一致,宽度自动调整。
    • BoxFit.scaleDown:类似于 contain,但如果图片原始尺寸小于组件,则不会放大图片。

2.2 对齐与重复

  • alignment:当图片尺寸小于组件,或者因为 fit 设置导致留有空白时,alignment 决定图片在组件内的对齐方式。例如 Alignment.centerAlignment.topLeft
  • repeat:如果图片尺寸小于组件,是否允许图片在水平和垂直方向上重复平铺。通常用于背景纹理。

2.3 颜色混合

  • color & colorBlendMode:这两个属性结合使用,可以为图片叠加一层颜色。color 指定要叠加的颜色,colorBlendMode 指定混合模式(如 BlendMode.multiplyBlendMode.srcIn)。这对于创建遮罩、改变图片色调或实现占位图效果非常有用。

示例:

Image.network(
  'https://example.com/avatar.jpg',
  width: 100,
  height: 100,
  fit: BoxFit.cover,          // 覆盖整个100x100区域,可能会裁剪
  alignment: Alignment.center,
  color: Colors.blue.withOpacity(0.3),
  colorBlendMode: BlendMode.multiply,
)

3. 图片缓存与性能

对于网络图片,Flutter 的 Image.network 默认会使用缓存。但其缓存机制是内存缓存,而不是磁盘缓存。这意味着图片在应用重启后会丢失,需要重新下载。

为了更好地控制缓存策略,尤其是对网络图片进行磁盘缓存,通常会使用 cached_network_image 这个第三方库。

使用 cached_network_image

  1. 在 pubspec.yaml 中添加依赖。
  2. 使用 CachedNetworkImage 组件。
CachedNetworkImage(
  imageUrl: 'https://example.com/large_image.jpg',
  placeholder: (context, url) => const CircularProgressIndicator(), // 加载中的占位组件
  errorWidget: (context, url, error) => const Icon(Icons.error),    // 加载失败的占位组件
  fit: BoxFit.cover,
)

这个库会将图片缓存到本地磁盘,并管理内存缓存,大大提升了图片加载的效率和用户体验。

4. 圆角与形状

要在 Flutter 中实现图片圆角或圆形头像,通常有两种方法:

方法一:使用 ClipRRect 或 CircleAvatar

将 Image 组件包裹在一个裁剪组件中。

  • ClipRRect:用于实现圆角矩形。

    ClipRRect(
      borderRadius: BorderRadius.circular(10.0),
      child: Image.network('https://p26-passport.byteacctimg.com/img/user-avatar/9447af82b1e48e41744bce604ab6ef1f~100x100.awebp'),
    )
    
  • CircleAvatar:专门用于实现圆形头像。

    CircleAvatar(
      backgroundImage: NetworkImage('https://p26-passport.byteacctimg.com/img/user-avatar/9447af82b1e48e41744bce604ab6ef1f~100x100.awebp'),
      radius: 30,
    )
    

方法二:使用 Container 的 decoration

这是更灵活、更常用的方法。将 Image 作为 Container 的背景图,并设置圆角。

Container(
  width: 100,
  height: 100,
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(15.0),
    image: const DecorationImage(
      image: NetworkImage('https://p26-passport.byteacctimg.com/img/user-avatar/9447af82b1e48e41744bce604ab6ef1f~100x100.awebp'),
      fit: BoxFit.cover,
    ),
  ),
)

这种方法的好处是可以在 BoxDecoration 中同时设置边框、阴影、渐变和背景图片。

5. 常见问题与最佳实践

  • 问题:图片变形

    • 原因:通常是因为没有正确使用 fit 属性,或者直接设置了 width 和 height 但没有指定 fit,导致图片被拉伸以适应给定的尺寸。
    • 解决:根据需要选择合适的 BoxFit 模式(如 covercontain)。
  • 问题:本地图片不显示

    • 原因:几乎总是因为 pubspec.yaml 文件配置错误,比如路径缩进不正确、文件路径写错或资源文件未包含在 assets 列表中。

    • 解决:仔细检查 pubspec.yaml 的格式。确保 assets: 部分位于 flutter: 下,并且路径缩进正确(通常为两个空格)。

      flutter:
        assets:
          - assets/images/  # 包含整个目录
          - assets/icons/logo.png  # 包含单个文件
      
  • 最佳实践:为网络图片指定占位图

    • 为了避免在图片加载完成前出现空白区域,使用 FadeInImage 或 cached_network_image 库来显示加载中的占位图,提供更平滑的用户体验。

    // 使用 FadeInImage (Flutter 内置)
    FadeInImage.assetNetwork(
      placeholder: 'assets/placeholder.png', // 本地占位图
      image: 'https://p26-passport.byteacctimg.com/img/user-avatar/9447af82b1e48e41744bce604ab6ef1f~100x100.awebp',
      fit: BoxFit.cover,
    )