Flutter可选择卡片插件flutter_selectable_cards的使用
Flutter可选择卡片插件flutter_selectable_cards的使用
简介
flutter_selectable_cards
是一个用于创建可选择卡片的Flutter插件,支持单选和多选功能。你可以使用这个插件轻松地为你的应用创建带有自定义样式的卡片,适用于引导页、表单或其他任何需要选择功能的场景。
功能特性
- 多选和单选支持:可以根据需求选择多选或单选模式。
- 任意Widget作为可选择项:可以将任何Flutter Widget作为可选择项。
- 自定义布局:支持Column、Row和Wrap三种布局方式。
- 内置简单卡片组件:提供了简单的卡片组件、带复选框/单选按钮的卡片组件以及带图片的卡片组件,也可以使用自定义的Widget。
- 与Flutter Widget无缝集成:易于与其他Flutter组件结合使用。
快速上手
1. 添加依赖
在 pubspec.yaml
文件中添加 flutter_selectable_cards
依赖:
dependencies:
flutter_selectable_cards: ^0.0.2
然后运行 flutter pub get
安装该包。
2. 导入并使用插件
在Dart文件中导入 flutter_selectable_cards
包:
import 'package:flutter_selectable_cards/flutter_selectable_cards.dart';
3. 使用 SelectableCard
组件
以下是一个简单的示例,展示了如何使用 SelectableCards
组件:
SelectableCards(
isMultipleSelection: false, // 设置为false表示单选,true表示多选
layout: const LayoutWrap(), // 使用Wrap布局
children: const [
SimpleCard(
index: 0,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('选择卡片1'),
),
),
SimpleCard(
index: 1,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('选择卡片2'),
),
),
],
onSelected: (index) {
print('主页面选择了卡片: $index');
},
)
4. 使用不同的布局
你可以根据需要选择不同的布局方式:
-
Column布局:
SelectableCards( layout: const LayoutColumn( crossAxisAlignment: CrossAxisAlignment.start, ), children: [], )
-
Row布局:
SelectableCards( layout: const LayoutRow(), children: [], )
-
Wrap布局:
SelectableCards( layout: const LayoutWrap( crossAxisAlignment: WrapCrossAlignment.center, ), children: [], )
完整示例Demo
以下是一个完整的示例,展示了如何使用 flutter_selectable_cards
插件来创建一个多选项卡的应用程序,每个选项卡展示了不同类型的选择卡片。
import 'package:flutter/material.dart';
import 'package:flutter_selectable_cards/flutter_selectable_cards.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<int> selectedImageMulti = [];
int selectedImageSingle = -1;
int anyWidgetSelected = -1;
int anyWidgetSelectedRow = -1;
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('选择卡片示例'),
),
body: DefaultTabController(
length: 4,
child: Column(
children: <Widget>[
const TabBar(
isScrollable: true,
tabs: [
Tab(text: '选择卡片'),
Tab(text: '单选卡片'),
Tab(text: '带图片的卡片'),
Tab(text: '任意Widget'),
],
),
Expanded(
child: TabBarView(
children: [
// 选择卡片示例
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'多选卡片',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Center(
child: SelectableCards(
isMultipleSelection: true,
layout: const LayoutWrap(crossAxisAlignment: WrapCrossAlignment.center),
children: const [
SimpleCard(
index: 0,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('多选卡片1'),
),
),
SimpleCard(
index: 1,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('多选卡片2'),
),
),
],
onSelected: (index) {
print('选择了卡片: $index');
},
),
),
const SizedBox(height: 20),
const Text(
'单选卡片',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Center(
child: SelectableCards(
isMultipleSelection: false,
layout: const LayoutWrap(crossAxisAlignment: WrapCrossAlignment.center),
children: const [
SimpleCard(
index: 0,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('单选卡片1'),
),
),
SimpleCard(
index: 1,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('单选卡片2'),
),
),
],
onSelected: (index) {
print('选择了单选卡片: $index');
},
),
),
const SizedBox(height: 20),
const Text(
'Column布局示例',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'多选Column布局',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
LayoutColumnExample(isMultipleSelection: true),
const SizedBox(height: 20),
const Text(
'单选Column布局',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
LayoutColumnExample(isMultipleSelection: false),
const SizedBox(height: 20),
const Text(
'Row布局示例',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'多选Row布局',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
LayoutRowExample(isMultipleSelection: true),
const SizedBox(height: 20),
const Text(
'单选Row布局',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
LayoutRowExample(isMultipleSelection: false),
const SizedBox(height: 20),
const Text(
'Wrap布局示例',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'多选Wrap布局',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
LayoutWrapExample(isMultipleSelection: true),
const SizedBox(height: 20),
const Text(
'单选Wrap布局',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
LayoutWrapExample(isMultipleSelection: false),
const SizedBox(height: 20),
],
),
),
),
// 单选卡片示例
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'多选卡片(带单选按钮设计)',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Center(
child: SelectableCards(
isMultipleSelection: true,
layout: const LayoutWrap(crossAxisAlignment: WrapCrossAlignment.center),
children: const [
RadioCard(
index: 0,
title: '单选卡片1',
subtitle: '单选卡片1副标题',
),
RadioCard(
index: 1,
title: '单选卡片2',
subtitle: '单选卡片2副标题',
),
],
onSelected: (index) {
print('选择了卡片: $index');
},
),
),
const SizedBox(height: 20),
const Text(
'单选卡片(带单选按钮设计)',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Center(
child: SelectableCards(
isMultipleSelection: false,
layout: const LayoutWrap(crossAxisAlignment: WrapCrossAlignment.center),
children: const [
RadioCard(
index: 0,
title: '单选卡片1',
subtitle: '单选卡片1副标题',
),
RadioCard(
index: 1,
title: '单选卡片2',
subtitle: '单选卡片2副标题',
),
],
onSelected: (index) {
print('选择了单选卡片: $index');
},
),
),
],
),
),
),
// 带图片的卡片示例
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'多选卡片(带图片)',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Center(
child: SelectableCards(
isMultipleSelection: true,
layout: const LayoutWrap(crossAxisAlignment: WrapCrossAlignment.center),
children: [
CardWithImage(
index: 0,
imageUrl: 'https://st2.depositphotos.com/1011969/6070/i/450/depositphotos_60704945-stock-photo-number-1.jpg',
text: '卡片1 ${selectedImageMulti.contains(0) ? '已选' : ''}',
),
CardWithImage(
index: 1,
imageUrl: 'https://st2.depositphotos.com/1011969/6070/i/450/depositphotos_60704955-stock-photo-number-2.jpg',
text: '卡片2 ${selectedImageMulti.contains(1) ? '已选' : ''}',
),
],
onSelected: (index) {
setState(() {
selectedImageMulti = index;
});
print('选择了卡片: $index');
},
),
),
const SizedBox(height: 20),
const Text(
'单选卡片(带图片)',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Center(
child: SelectableCards(
isMultipleSelection: false,
layout: const LayoutWrap(crossAxisAlignment: WrapCrossAlignment.center),
children: [
CardWithImage(
index: 0,
imageUrl: 'https://st2.depositphotos.com/1011969/6070/i/450/depositphotos_60704945-stock-photo-number-1.jpg',
text: '单选卡片1 ${selectedImageSingle == 0 ? '已选' : ''}',
),
CardWithImage(
index: 1,
imageUrl: 'https://st2.depositphotos.com/1011969/6070/i/450/depositphotos_60704955-stock-photo-number-2.jpg',
text: '单选卡片2 ${selectedImageSingle == 1 ? '已选' : ''}',
),
],
onSelected: (index) {
setState(() {
selectedImageSingle = index;
});
print('选择了单选卡片: $index');
},
),
),
],
),
),
),
// 任意Widget示例
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'任意Widget',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'任何Widget都可以用作SelectableCards的子项',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'容器示例',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
SelectableCards(
isMultipleSelection: false,
layout: const LayoutWrap(crossAxisAlignment: WrapCrossAlignment.center),
children: [
Container(
width: 100,
height: 100,
color: Colors.red,
child: Center(
child: Text(
'容器1 ${anyWidgetSelected == 0 ? '已选' : ''}'),
),
),
Container(
width: 100,
height: 100,
color: Colors.blue,
child: Center(
child: Text(
'容器2 ${anyWidgetSelected == 1 ? '已选' : ''}'),
),
),
],
onSelected: (index) {
setState(() {
anyWidgetSelected = index;
});
print('选择了卡片: $index');
},
),
const SizedBox(height: 20),
const Text(
'Text in LayoutRow 示例',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
SelectableCards(
isMultipleSelection: false,
layout: const LayoutRow(
mainAxisAlignment: MainAxisAlignment.center,
),
children: [
Container(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Text(
'容器1 ${anyWidgetSelectedRow == 0 ? '已选' : ''}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
Container(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Text(
'容器2 ${anyWidgetSelectedRow == 1 ? '已选' : ''}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
],
onSelected: (index) {
setState(() {
anyWidgetSelectedRow = index;
});
print('选择了卡片: $index');
},
),
const SizedBox(height: 20),
const Text(
'任何Widget都可以用作SelectableCards的子项,并且可以用于多选或单选',
),
],
),
),
),
],
),
),
],
),
),
),
);
}
}
class LayoutColumnExample extends StatelessWidget {
final bool isMultipleSelection;
const LayoutColumnExample({required this.isMultipleSelection});
[@override](/user/override)
Widget build(BuildContext context) {
return SelectableCards(
isMultipleSelection: isMultipleSelection,
layout: const LayoutColumn(
mainAxisAlignment: MainAxisAlignment.center,
),
children: const [
SimpleCard(
index: 0,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Column卡片1'),
),
),
SimpleCard(
index: 1,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Column卡片2'),
),
),
],
onSelected: (index) {
print('LayoutColumnExample选择了卡片: $index');
},
);
}
}
class LayoutRowExample extends StatelessWidget {
final bool isMultipleSelection;
const LayoutRowExample({required this.isMultipleSelection});
[@override](/user/override)
Widget build(BuildContext context) {
return SelectableCards(
isMultipleSelection: isMultipleSelection,
layout: const LayoutRow(
mainAxisAlignment: MainAxisAlignment.center,
),
children: const [
SimpleCard(
index: 0,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Row卡片1'),
),
),
SimpleCard(
index: 1,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Row卡片2'),
),
),
],
onSelected: (index) {
print('LayoutRowExample选择了卡片: $index');
},
);
}
}
class LayoutWrapExample extends StatelessWidget {
final bool isMultipleSelection;
const LayoutWrapExample({required this.isMultipleSelection});
[@override](/user/override)
Widget build(BuildContext context) {
return SelectableCards(
isMultipleSelection: isMultipleSelection,
layout: const LayoutWrap(
crossAxisAlignment: WrapCrossAlignment.center,
),
children: const [
SimpleCard(
index: 0,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Wrap卡片1'),
),
),
SimpleCard(
index: 1,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Wrap卡片2'),
),
),
],
onSelected: (index) {
print('LayoutWrapExample选择了卡片: $index');
},
);
}
}
更多关于Flutter可选择卡片插件flutter_selectable_cards的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter可选择卡片插件flutter_selectable_cards的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用flutter_selectable_cards
插件的一个简单示例。这个插件允许用户从一个卡片集合中选择一个或多个卡片。
首先,确保你已经在pubspec.yaml
文件中添加了flutter_selectable_cards
依赖:
dependencies:
flutter:
sdk: flutter
flutter_selectable_cards: ^最新版本号 # 替换为实际最新版本号
然后运行flutter pub get
来安装依赖。
接下来,你可以在你的Flutter应用中使用这个插件。以下是一个简单的示例代码,展示如何创建一个可选择卡片的界面:
import 'package:flutter/material.dart';
import 'package:flutter_selectable_cards/flutter_selectable_cards.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Selectable Cards Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SelectableCardsScreen(),
);
}
}
class SelectableCardsScreen extends StatefulWidget {
@override
_SelectableCardsScreenState createState() => _SelectableCardsScreenState();
}
class _SelectableCardsScreenState extends State<SelectableCardsScreen> {
List<String> selectedCards = [];
@override
Widget build(BuildContext context) {
List<CardModel> cards = [
CardModel(
title: 'Card 1',
description: 'This is the first card.',
image: NetworkImage('https://via.placeholder.com/150'),
),
CardModel(
title: 'Card 2',
description: 'This is the second card.',
image: NetworkImage('https://via.placeholder.com/150'),
),
CardModel(
title: 'Card 3',
description: 'This is the third card.',
image: NetworkImage('https://via.placeholder.com/150'),
),
];
return Scaffold(
appBar: AppBar(
title: Text('Selectable Cards Demo'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SelectableCards(
cards: cards,
onSelectedCardChanged: (List<int> selectedIndexes) {
setState(() {
selectedCards = selectedIndexes
.map((index) => cards[index].title)
.toList();
});
},
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Selected Cards: ${selectedCards.join(", ")}'),
],
),
),
);
}
}
class CardModel {
String title;
String description;
ImageProvider image;
CardModel({required this.title, required this.description, required this.image});
}
在这个示例中,我们定义了一个CardModel
类来表示卡片的数据,包括标题、描述和图片。然后在SelectableCardsScreen
中,我们创建了一个卡片列表,并使用SelectableCards
小部件来显示这些卡片。
当用户选择卡片时,onSelectedCardChanged
回调会被触发,我们在这个回调中更新selectedCards
列表,并在底部导航栏中显示选中的卡片标题。
请注意,由于flutter_selectable_cards
插件的具体API可能会随着版本更新而变化,因此请务必参考最新的官方文档和示例代码来确保代码的正确性。如果插件的API有所变化,可能需要调整上述代码以匹配新的API。