Flutter步进器插件expansion_stepper的使用

发布于 1周前 作者 ionicwang 来自 Flutter

Flutter步进器插件expansion_stepper的使用

1. 插件简介

expansion_stepper 是一个用于创建现代水平和垂直步进器的Flutter插件,结合了 ExpansionTileStepper 组件,提供了Material设计风格的视觉体验。该插件允许开发者创建可扩展的卡片,每个卡片包含多个步骤,用户可以通过点击卡片来展开或折叠内容。

2. 功能特性

  • 步进器卡片:应用展示了两个标记为 ‘A’ 和 ‘B’ 的扩展步进器卡片,每个卡片包含多个步骤,表示一个流程的不同阶段。
  • 卡片交互:用户可以通过点击卡片来展开或折叠内容,还可以通过按钮编程控制其他卡片的状态(打开、关闭、切换)。
  • 步进器子项:每个卡片包含多个步进器子项,表示流程中的不同步骤,如订单放置、订单准备、配送进度和订单交付。
  • 调试信息:在 onTap 回调中包含了调试语句,可以在调试模式下打印消息到控制台。

3. 使用步骤

3.1 添加依赖

pubspec.yaml 文件的 dependencies 部分添加以下行:

dependencies:
  ...
  expansion_stepper: ^latest_version
3.2 导入包

在你的Dart文件中导入 expansion_stepper 包:

import 'package:expansion_stepper/expansion_stepper.dart';

4. 完整示例代码

以下是一个完整的示例代码,展示了如何使用 ExpansionStepperCard 创建带有步进器的卡片:

import 'package:expansion_stepper/expansion_stepper.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Expansion Stepper Card Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      home: const MyHomePage(title: 'Expansion Stepper Card Demo'),
    );
  }
}

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

  final String? title;

  [@override](/user/override)
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  // 创建两个全局键,用于控制卡片的状态
  final GlobalKey<ExpansionStepperCardState> cardA = GlobalKey();
  final GlobalKey<ExpansionStepperCardState> cardB = GlobalKey();

  [@override](/user/override)
  Widget build(BuildContext context) {
    // 定义按钮样式
    final ButtonStyle flatButtonStyle = TextButton.styleFrom(
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
    );

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: ListView(
        children: <Widget>[
          // 卡片 A
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 12.0),
            child: ExpansionStepperCard(
              key: cardA,
              leading: const CircleAvatar(child: Text('A')), // 左侧图标
              title: const Text('Tap me!'), // 标题
              subtitle: const Text('I expand!'), // 副标题
              stepperChildren: [
                // 步骤 1: 订单已下单
                StepperChildren(
                  title: const Text(
                    "Order Placed",
                    style: TextStyle(color: Colors.grey),
                  ),
                  subtitle: const Text("Your order has been placed"),
                  iconWidget: Container(
                    padding: const EdgeInsets.all(8),
                    decoration: const BoxDecoration(
                      color: Colors.green,
                      borderRadius: BorderRadius.all(Radius.circular(30)),
                    ),
                    child: const Icon(Icons.looks_one, color: Colors.white),
                  ),
                  onTap: () {
                    if (kDebugMode) {
                      print('Order Placed!');
                    }
                  },
                  padding: const EdgeInsets.only(left: 16),
                  topContent: const Divider(
                    thickness: 1,
                    color: Colors.black,
                  ),
                ),
                // 步骤 2: 准备中
                StepperChildren(
                  title: const Text("Preparing"),
                  subtitle: const Text("Your order is being prepared"),
                  iconWidget: Container(
                    padding: const EdgeInsets.all(8),
                    decoration: const BoxDecoration(
                      color: Colors.green,
                      borderRadius: BorderRadius.all(Radius.circular(30)),
                    ),
                    child: const Icon(Icons.looks_two, color: Colors.white),
                  ),
                  onTap: () {
                    if (kDebugMode) {
                      print('Order Preparing!');
                    }
                  },
                  padding: const EdgeInsets.only(left: 16),
                ),
                // 步骤 3: 配送中
                StepperChildren(
                  title: const Text("On the way"),
                  subtitle: const Text(
                      "Our delivery executive is on the way to deliver your item"),
                  iconWidget: Container(
                    padding: const EdgeInsets.all(8),
                    decoration: const BoxDecoration(
                      color: Colors.green,
                      borderRadius: BorderRadius.all(Radius.circular(30)),
                    ),
                    child: const Icon(Icons.looks_3, color: Colors.white),
                  ),
                  onTap: () {
                    if (kDebugMode) {
                      print('On the way!');
                    }
                  },
                  padding: const EdgeInsets.only(left: 16),
                ),
                // 步骤 4: 已送达
                StepperChildren(
                  title: const Text("Delivered", style: TextStyle(color: Colors.grey)),
                  onTap: () {
                    if (kDebugMode) {
                      print('Delivered!');
                    }
                  },
                  padding: const EdgeInsets.only(left: 16),
                ),
              ],
              children: [
                const Divider(thickness: 1.0, height: 1.0),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
                    child: Text(
                      """Hi there, I'm a drop-in replacement for Flutter's ExpansionTile.

Use me any time you think your app could benefit from being just a bit more Material.

These buttons control the next card down!""",
                      style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 16),
                    ),
                  ),
                ),
                ButtonBar(
                  alignment: MainAxisAlignment.spaceAround,
                  buttonHeight: 52.0,
                  buttonMinWidth: 90.0,
                  children: <Widget>[
                    TextButton(
                      style: flatButtonStyle,
                      onPressed: () {
                        cardB.currentState?.expand(); // 打开卡片 B
                      },
                      child: const Column(
                        children: <Widget>[
                          Icon(Icons.arrow_downward),
                          Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
                          Text('Open'),
                        ],
                      ),
                    ),
                    TextButton(
                      style: flatButtonStyle,
                      onPressed: () {
                        cardB.currentState?.collapse(); // 关闭卡片 B
                      },
                      child: const Column(
                        children: <Widget>[
                          Icon(Icons.arrow_upward),
                          Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
                          Text('Close'),
                        ],
                      ),
                    ),
                    TextButton(
                      style: flatButtonStyle,
                      onPressed: () {
                        cardB.currentState?.toggleExpansion(); // 切换卡片 B 的状态
                      },
                      child: const Column(
                        children: <Widget>[
                          Icon(Icons.swap_vert),
                          Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
                          Text('Toggle'),
                        ],
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
          // 卡片 B
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 12.0),
            child: ExpansionStepperCard(
              key: cardB,
              expandedTextColor: Colors.red, // 展开时文本颜色
              leading: const CircleAvatar(child: Text('B')), // 左侧图标
              title: const Text('Tap me!'), // 标题
              subtitle: const Text('I expand, too!'), // 副标题
              children: <Widget>[
                const Divider(thickness: 1.0, height: 1.0),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
                    child: Text(
                      """Hi there, I'm a drop-in replacement for Flutter's ExpansionTile.

Use me any time you think your app could benefit from being just a bit more Material.

These buttons control the card above!""",
                      style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 16),
                    ),
                  ),
                ),
                ButtonBar(
                  alignment: MainAxisAlignment.spaceAround,
                  buttonHeight: 52.0,
                  buttonMinWidth: 90.0,
                  children: <Widget>[
                    TextButton(
                      style: flatButtonStyle,
                      onPressed: () {
                        cardA.currentState?.expand(); // 打开卡片 A
                      },
                      child: const Column(
                        children: <Widget>[
                          Icon(Icons.arrow_downward),
                          Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
                          Text('Open'),
                        ],
                      ),
                    ),
                    TextButton(
                      style: flatButtonStyle,
                      onPressed: () {
                        cardA.currentState?.collapse(); // 关闭卡片 A
                      },
                      child: const Column(
                        children: <Widget>[
                          Icon(Icons.arrow_upward),
                          Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
                          Text('Close'),
                        ],
                      ),
                    ),
                    TextButton(
                      style: flatButtonStyle,
                      onPressed: () {
                        cardA.currentState?.toggleExpansion(); // 切换卡片 A 的状态
                      },
                      child: const Column(
                        children: <Widget>[
                          Icon(Icons.swap_vert),
                          Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
                          Text('Toggle'),
                        ],
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

更多关于Flutter步进器插件expansion_stepper的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter步进器插件expansion_stepper的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用expansion_stepper插件来实现步进器的示例代码。expansion_stepper是一个流行的Flutter插件,用于创建具有扩展面板功能的步进器界面。

首先,确保你已经在pubspec.yaml文件中添加了expansion_stepper依赖:

dependencies:
  flutter:
    sdk: flutter
  expansion_stepper: ^latest_version  # 替换为最新版本号

然后运行flutter pub get来安装依赖。

以下是一个完整的示例代码,展示了如何使用expansion_stepper

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Expansion Stepper Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Expansion Stepper Demo'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: MyStepper(),
        ),
      ),
    );
  }
}

class MyStepper extends StatefulWidget {
  @override
  _MyStepperState createState() => _MyStepperState();
}

class _MyStepperState extends State<MyStepper> {
  final List<Step> _steps = [
    Step(
      title: Text('Step 1: Personal Info'),
      content: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          TextField(
            decoration: InputDecoration(labelText: 'First Name'),
          ),
          SizedBox(height: 16),
          TextField(
            decoration: InputDecoration(labelText: 'Last Name'),
          ),
        ],
      ),
    ),
    Step(
      title: Text('Step 2: Contact Info'),
      content: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          TextField(
            decoration: InputDecoration(labelText: 'Email'),
            keyboardType: TextInputType.emailAddress,
          ),
          SizedBox(height: 16),
          TextField(
            decoration: InputDecoration(labelText: 'Phone Number'),
            keyboardType: TextInputType.phone,
          ),
        ],
      ),
    ),
    Step(
      title: Text('Step 3: Confirm'),
      content: Text('Please review your information and confirm.'),
    ),
  ];

  int _currentStep = 0;

  void _nextStep() {
    setState(() {
      if (_currentStep < _steps.length - 1) {
        _currentStep++;
      }
    });
  }

  void _previousStep() {
    setState(() {
      if (_currentStep > 0) {
        _currentStep--;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return ExpansionStepper(
      steps: _steps,
      currentStep: _currentStep,
      controlsBuilder: (BuildContext context, {
        VoidCallback? onNext,
        VoidCallback? onPrevious,
        VoidCallback? onCancel,
      }) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            if (_currentStep > 0)
              ElevatedButton(
                onPressed: onPrevious,
                child: Text('Previous'),
              ),
            Expanded(child: SizedBox()),
            if (_currentStep < _steps.length - 1)
              ElevatedButton(
                onPressed: onNext,
                child: Text('Next'),
              ),
            if (_currentStep == _steps.length - 1)
              ElevatedButton(
                onPressed: () {
                  // Handle the confirm action here
                  print('All steps completed!');
                },
                child: Text('Confirm'),
              ),
          ],
        );
      },
      onStepContinue: _nextStep,
      onStepCancel: () {
        // Handle cancel action here if needed
        setState(() {
          _currentStep = 0;
        });
      },
    );
  }
}

在这个示例中,我们定义了一个包含三个步骤的步进器:

  1. 第一个步骤收集个人信息。
  2. 第二个步骤收集联系信息。
  3. 第三个步骤是一个确认步骤,显示用户输入的信息。

每个步骤都有一个标题和内容。内容部分包含了一些TextField小部件,用于收集用户输入。底部控制按钮允许用户在步骤之间导航,并在最后一个步骤进行确认操作。

注意:ExpansionStepper与标准的Stepper有所不同,它允许每个步骤的内容在点击时展开和折叠。这个示例代码演示了基本的用法,你可以根据需要进行进一步的自定义和扩展。

回到顶部