Flutter标签页管理插件blossom_tabs的使用
Flutter标签页管理插件blossom_tabs的使用
特性
- 您可以拖拽并重新排序标签页。
- 动态地在运行时添加标签页。
- 保存当前标签页管理器的状态以便下次使用(例如,在应用重启时)。
- 自定义外观和行为。
开始使用
首先,在您的项目中添加插件:
blossom_tabs: ^4.0.0
然后导入该插件:
import 'package:blossom_tabs/blossom_tabs.dart';
使用方法
您可以在widget树中这样添加:
// 配置`controller`
var _controller = BlossomTabController<int>(tabs: []); // 推断数据类型以方便访问
return BlossomTabControllerScope<int>(
controller: _controller,
child: Scaffold(
appBar: BlossomTabBar<int>(
height: 48,
selectedColor: Colors.blue,
stickyColor: Colors.white,
backgroundColor: Colors.blue.withOpacity(0.3),
dividerColor: Colors.blue,
tabBuilder: (context, tab, isActive) => Text(tab.id), // 显示标签页的ID
),
body: BlossomTabView<int>(
builder: (tab) => Text(tab.id) // 显示标签页的内容
),
),
);
示例代码
以下是一个完整的示例代码,展示了如何使用blossom_tabs
插件:
import 'dart:convert';
import 'dart:math';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:blossom_tabs/blossom_tabs.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
doWhenWindowReady(() {
const initialSize = Size(800, 450);
appWindow.minSize = initialSize;
appWindow.size = initialSize;
appWindow.alignment = Alignment.center;
appWindow.show();
});
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Widget buildTab(
BuildContext context, {
required bool isActive,
bool useRow = true,
Widget? icon,
Widget? activeIcon,
String? title,
TextStyle? style,
TextStyle? activeStyle,
}) {
var children = [
(isActive ? activeIcon ?? icon : icon) ??
const SizedBox(
width: 10,
),
if (title != null)
Flexible(
child: Text(
title,
softWrap: false,
overflow: TextOverflow.fade,
style: isActive ? activeStyle ?? style : style,
),
),
];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: useRow
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(children: children),
],
)
: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
],
),
);
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _controller = BlossomTabController<int>(tabs: []);
var _tabs = <BlossomTab<int>>[];
BlossomTab<int> _getTab(String e) => BlossomTab<int>(
id: e,
data: int.parse(e.codeUnits.join()),
title: e.toUpperCase(),
isSticky: e == 'd',
);
@override
void initState() {
_tabs = ['a', 'b', 'c', 'd', 'e']
.map(
(e) => _getTab(e),
)
.toList();
_controller = BlossomTabController<int>(currentTab: 'b', tabs: _tabs);
super.initState();
}
@override
Widget build(BuildContext context) {
return BlossomTabControllerScope(
controller: _controller,
child: Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(85),
child: Stack(
children: [
MoveWindow(
child: BlossomTabBar<int>(
height: 85,
bottomBarHeight: 40,
selectedColor: Colors.blue,
dragColor: Colors.blue.withOpacity(0.6),
stickyColor: Colors.white,
backgroundColor: Colors.blue.withOpacity(0.3),
dividerColor: Colors.blue,
bottomColor: Colors.blue,
margin: const EdgeInsets.only(left: 4, top: 4, right: 140),
tabBarMargin: 4,
tabBuilder: (context, tab, isActive) => buildTab(
context,
isActive: isActive,
title: tab.id,
activeStyle:
tab.id == 'd' ? null : const TextStyle(color: Colors.white),
icon: tab.id == 'd'
? null
: const Padding(
padding: EdgeInsets.all(4.0),
child: Icon(
Icons.ac_unit,
size: 14,
color: Colors.black26,
),
),
activeIcon: tab.id == 'd'
? null
: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.ac_unit,
size: 14,
color: Colors.white,
),
),
),
tabActions: (context, tab) => [
if (tab.id != 'd')
Listener(
onPointerDown: (_) {
_controller.removeTabById(tab.id);
},
child: const Padding(
padding: EdgeInsets.all(4.0),
child: Icon(
Icons.close,
size: 14,
color: Colors.white,
),
),
),
],
bottomBar: BlossomTabControllerScopeDescendant<int>(
builder: (context, controller) {
// Future.delayed(Duration.zero)
// .then((_) => print(jsonEncode(controller.toJson())));
return Container(
color: controller.currentTab == 'd' ? Colors.white : null,
);
}),
actions: [
Padding(
padding: const EdgeInsets.only(left: 6.0),
child: NewTabBtn(
onTap: () {
final z = _controller.tabs.map((e) => e.id).toList()..sort();
var c = z.isEmpty ? 'a' : z.last;
final lastCharacter =
String.fromCharCode(c.codeUnitAt(c.length - 1) + 1);
c = c.substring(0, c.length - 1) + lastCharacter;
_controller.addTab(_getTab(c));
},
),
)
],
),
),
Positioned(
right: 0,
child: WindowButtons(),
),
],
),
),
body: Row(
children: [
BlossomVerticalTabBar<int>(
width: 240,
sideBarWidth: 180,
selectedColor: Colors.blue,
dragColor: Colors.blue.withOpacity(0.6),
stickyColor: Colors.white,
backgroundColor: Colors.blue.withOpacity(0.3),
dividerColor: Colors.blue,
sideBarColor: Colors.blue,
margin: const EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 40),
tabBarMargin: 0,
showIndicator: true,
indicatorColor: Colors.white,
tabBuilder: (context, tab, isActive) => buildTab(
context,
isActive: isActive,
useRow: false,
title: tab.id,
activeStyle: tab.id == 'd' ? null : const TextStyle(color: Colors.white),
activeIcon: tab.id == 'd'
? null
: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.ac_unit,
size: 14,
color: Colors.white,
),
),
),
sideBar: BlossomTabControllerScopeDescendant<int>(
builder: (context, controller) {
// Future.delayed(Duration.zero)
// .then((_) => print(jsonEncode(controller.toJson())));
return Container(
color: controller.currentTab == 'd' ? Colors.white : null,
);
}),
actions: [
Padding(
padding: const EdgeInsets.only(top: 6.0),
child: NewTabBtn(
onTap: () {
final z = _controller.tabs.map((e) => e.id).toList()..sort();
var c = z.isEmpty ? 'a' : z.last;
final lastCharacter =
String.fromCharCode(c.codeUnitAt(c.length - 1) + 1);
c = c.substring(0, c.length - 1) + lastCharacter;
_controller.addTab(_getTab(c));
},
),
)
],
),
Expanded(
child: BlossomTabView<int>(
builder: (tab) => ColorBox(
child: Center(child: ColorBox(child: Center(child: Text(tab.id)))),
),
),
),
],
),
),
);
}
}
class NewTabBtn extends StatefulWidget {
const NewTabBtn({
Key? key,
required this.onTap,
}) : super(key: key);
final void Function() onTap;
@override
State<NewTabBtn> createState() => _NewTabBtnState();
}
class _NewTabBtnState extends State<NewTabBtn> {
var _opacity = 0.1;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: widget.onTap,
onHover: (h) => setState(() => _opacity = h ? 0.3 : 0.1),
child: Container(
padding: const EdgeInsets.all(2.0),
color: Colors.blue.withOpacity(_opacity),
child: const Icon(
Icons.add,
size: 22,
color: Colors.white,
),
),
);
}
}
class ColorBox extends StatefulWidget {
const ColorBox({Key? key, this.child}) : super(key: key);
final Widget? child;
@override
_ColorBoxState createState() => _ColorBoxState();
}
class _ColorBoxState extends State<ColorBox> {
Color? _color;
_randomColor() => Color(0xFF000000 + Random().nextInt(0x00FFFFFF));
@override
void initState() {
_color = _randomColor();
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_color = _randomColor();
});
},
child: Container(width: 150, height: 150, color: _color, child: widget.child),
);
}
}
final buttonColors = WindowButtonColors(
iconNormal: Colors.blue,
mouseOver: Colors.blue.withOpacity(0.2),
mouseDown: Colors.blue,
);
final closeButtonColors = WindowButtonColors(
mouseOver: Colors.red.withOpacity(0.9),
mouseDown: Colors.red,
iconNormal: Colors.blue,
);
class WindowButtons extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
MinimizeWindowButton(colors: buttonColors),
MaximizeWindowButton(colors: buttonColors),
CloseWindowButton(colors: closeButtonColors),
],
);
}
}
其他信息
此外,您可以使用BlossomTabControllerScopeDescendant
监听标签页状态的变化,例如:
BlossomTabControllerScopeDescendant<int>(
builder: (context, controller) {
return Container(
color: controller.currentTab == 'd' ? Colors.white : Colors.blue,
);
});
更多关于Flutter标签页管理插件blossom_tabs的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter标签页管理插件blossom_tabs的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用Flutter插件blossom_tabs
来管理标签页的示例代码。blossom_tabs
是一个用于在Flutter应用中创建和管理标签页的插件。以下示例展示了如何集成和使用这个插件。
首先,确保你已经在pubspec.yaml
文件中添加了blossom_tabs
依赖:
dependencies:
flutter:
sdk: flutter
blossom_tabs: ^latest_version # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,你可以按照以下步骤在你的Flutter应用中使用blossom_tabs
:
- 导入包
在你的Dart文件中导入blossom_tabs
包:
import 'package:blossom_tabs/blossom_tabs.dart';
- 定义标签页内容
定义你想要在每个标签页中显示的内容。例如,创建两个简单的页面:
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Home Page'));
}
}
class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Settings Page'));
}
}
- 使用BlossomTabs
在你的主应用中使用BlossomTabs
来管理这些标签页。例如,在MyApp
中:
import 'package:flutter/material.dart';
import 'package:blossom_tabs/blossom_tabs.dart';
// 导入你定义的页面
import 'home_page.dart';
import 'settings_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('BlossomTabs Example'),
),
body: BlossomTabs(
tabs: [
BlossomTab(
title: 'Home',
icon: Icon(Icons.home),
child: HomePage(),
),
BlossomTab(
title: 'Settings',
icon: Icon(Icons.settings),
child: SettingsPage(),
),
],
initialIndex: 0, // 初始选中的标签页索引
onTabChanged: (index) {
// 可选:标签页切换时的回调
print('Selected tab index: $index');
},
),
),
);
}
}
在这个示例中:
BlossomTabs
组件用于管理多个标签页。BlossomTab
对象定义了每个标签页的标题、图标和内容。initialIndex
参数指定了初始选中的标签页索引。onTabChanged
是一个可选的回调,当标签页切换时会触发。
- 运行应用
现在,你可以运行你的Flutter应用,应该会看到一个包含两个标签页的界面,点击不同的标签可以切换显示的内容。
这个示例展示了如何使用blossom_tabs
插件来创建和管理标签页。根据实际需求,你可以进一步自定义标签页的外观和行为。