如何在 Flutter 中制作具有炫酷效果的自定义单选按钮

211 阅读4分钟

先决条件

  • Dart 编程语言的基础知识。
  • 熟悉 Flutter 框架及其小部件。
  • Flutter SDK 和 Android Studio 或 VS Code 的有效安装
  • 了解有状态的小部件及其在 Flutter 应用程序中管理状态的作用。
  • Flutter布局定位基础知识
  • 熟悉在 Flutter 中使用图像资源。

教程

打开任一 IDE。创建一个 flutter 项目(应用程序)并将项目命名为您选择的任何名称。

创建项目后,在您的设备或模拟器上运行该应用程序。

下载三个支付平台的标识和对勾圆圈图标。我从谷歌上的图片下载了它们。

将四个图像添加到assets文件夹中,或者您可能需要在项目文件夹的根目录下创建文件夹。
assets/images/

将图像的路径添加到 pubspec.yaml

 # To add assets to your application, add an assets section, like this:
assets:
    - assets/images/visa.png
    - assets/images/mastercard.png
    - assets/images/paypal.png
    - assets/images/tick-circle.png

在 main.dart 文件中,将您的代码替换为以下代码。该代码设置了一个基本的 Flutter 应用程序,主页上有一个应用程序栏和一个空容器。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: const Color(0xFF5C6ED1),
      ),
      home: const HomePage(),
    );
  }
}
class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
    @override
    Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text("Custom Radio Button Demo"),
          ),
          body: Container();
     }
}

该类HomePage是一个有状态的小部件,具有 类型的状态对象_HomePageState。此类的方法build()返回一个Scaffold小部件,它是应用程序的基本视觉结构。脚手架小部件有一个AppBar和一个body。标题AppBar带有文本“自定义单选按钮演示”,正文为空Container

...
class _HomePageState extends State<HomePage> {
  int selectedPayment = 0;

  Widget CustomPaymentCardButton(String assetName, int index) {
    return OutlinedButton(
      onPressed: () {
        setState(() {
          selectedPayment = index;
        });
      },
      style: OutlinedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
        shape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
        side: BorderSide(
            width: (selectedPayment == index) ? 2.0 : 0.5,
            color: (selectedPayment == index)
                ? Colors.green
                : Colors.blue.shade600),
      ),
      child: Stack(
        children: [
          Center(
            child: Image.asset(
              assetName,
              fit: BoxFit.contain,
              width: 120,
              height: 120,
            ),
          ),
          if (selectedPayment == index)
            Positioned(
              top: 5,
              right: 5,
              child: Image.asset(
                "assets/images/tick-circle.png",
                width: 25,
                fit: BoxFit.cover,
              ),
            ),
        ],
      ),
    );
  }
...

_HomePageState在which extends类里面State。有一个名为selectedPayment初始设置为 0 的变量。它用于跟踪所选的支付选项。有一个方法CustomPaymentCardButton有两个参数,assetName 和 index。assetName 是表示图像资产路径的字符串,index 是表示按钮在支付选项列表中的位置的整数。

该方法返回一个OutlinedButton小部件,它是一个带有轮廓边框的 Material Design 风格按钮。按钮的回调onPressed将 的值设置selectedPayment为按下的按钮的索引。

child按钮的属性是一个 Stack 小部件,其中包含一个带有Image.asset参数指定图像的小部件assetName。它还Stack包含一个 Positioned 小部件,如果按钮被选中,它会显示一个复选标记图标。

按钮的属性style用于根据是否选中来设置其填充、边框形状和颜色。如果按钮被选中,其边框宽度设置为 2.0,颜色设置为绿色。否则,其边框宽度设置为 0.5,颜色设置为蓝色。

...
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Custom Radio Button Demo"),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20),
        child: Column(
          children: [
            const SizedBox(
              height: 20,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  child: CustomPaymentCardButton("assets/images/visa.png", 0),
                ),
                const SizedBox(
                  width: 20,
                ),
                Expanded(
                  child: CustomPaymentCardButton(
                      "assets/images/mastercard.png", 1),
                ),
              ],
            ),
            const SizedBox(
              height: 16,
            ),
            CustomPaymentCardButton("assets/images/paypal.png", 2),
          ],
        ),
      ),
    );
  }
...

正文是使用Padding水平填充为 20 的小部件定义的。填充包含一个包含Column支付选项列表的小部件。

Row小部件包含两个Expanded小部件,每个小部件都包含一个使用该CustomPaymentCardButton方法定义的自定义支付卡按钮。第一个按钮显示 Visa 卡的图像,索引为 0。第二个按钮显示万事达卡的图像,索引为 1。该小部件用于并排显示两个Row按钮宽度。

CustomPaymentCardButton使用 PayPal 徽标的图像和 2 的索引再次调用该方法,以显示 下方的第三个按钮Row

整体代码如下:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: const Color(0xFF5C6ED1),
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int selectedPayment = 0;

  Widget CustomPaymentCardButton(String assetName, int index) {
    return OutlinedButton(
      onPressed: () {
        setState(() {
          selectedPayment = index;
        });
      },
      style: OutlinedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
        shape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
        side: BorderSide(
            width: (selectedPayment == index) ? 2.0 : 0.5,
            color: (selectedPayment == index)
                ? Colors.green
                : Colors.blue.shade600),
      ),
      child: Stack(
        children: [
          Center(
            child: Image.asset(
              assetName,
              fit: BoxFit.contain,
              width: 120,
              height: 120,
            ),
          ),
          if (selectedPayment == index)
            Positioned(
              top: 5,
              right: 5,
              child: Image.asset(
                "assets/images/tick-circle.png",
                width: 25,
                fit: BoxFit.cover,
              ),
            ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Custom Radio Button Demo"),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20),
        child: Column(
          children: [
            const SizedBox(
              height: 20,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  child: CustomPaymentCardButton("assets/images/visa.png", 0),
                ),
                const SizedBox(
                  width: 20,
                ),
                Expanded(
                  child: CustomPaymentCardButton(
                      "assets/images/mastercard.png", 1),
                ),
              ],
            ),
            const SizedBox(
              height: 16,
            ),
            CustomPaymentCardButton("assets/images/paypal.png", 2),
          ],
        ),
      ),
    );
  }
}

运行应用程序。

整体代码创建了一个应用程序,该应用程序显示可以选择的自定义支付卡按钮,并显示所选支付选项的刻度线。

结论

总之,自定义单选按钮可以提供一种增强 Flutter 应用程序用户体验的好方法。通过使用自定义小部件并仔细选择视觉提示,您可以创建突出的单选按钮并将其目的传达给您的用户。