一、Button 基础组件
Flutter开发中用 ElevatedButton、OutlinedButton、TextButton配置麻烦且不支持渐进色背景,于是通过子类的方式实现了常见几种样式按钮封装。好处是代码精简,支持主题文件统一配置全局效果。效果如下:
二、示例代码
1、ThemeData 配置
extensions: [
AppTheme(
primary: const Color(0xFF00B451),
primary2: const Color(0xFF00B451).withOpacity(0.8),
bgColor: const Color(0xFFF3F3F3),
fontColor: const Color(0xFF1A1A1A),
titleStyle: const TextStyle(
color: Color(0xFF1A1A1A),
fontSize: 18,
fontWeight: FontWeight.w500,
decoration: TextDecoration.none,
),
textStyle: const TextStyle(
color: Color(0xFF1A1A1A),
fontSize: 16,
fontWeight: FontWeight.w400,
decoration: TextDecoration.none,
),
cancelColor: const Color(0xFFE65F55),
lineColor: const Color(0xffE4E4E4),
borderColor: const Color(0xFFE5E5E5),
disabledColor: const Color(0xffB3B3B3),
),
NButtonTheme(
primary: const Color(0xFF00B451),//按钮主题色(绿)
),
],
2、按钮效果展示
NSectionHeader(
title: "NButton",
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
...[
NButton(
title: "NButton",
onPressed: () {
DLog.d("NButton");
},
),
NButton(
title: "NButton: 禁用",
enable: false,
onPressed: () {},
),
NButton(
title: "NButton: red",
primary: Colors.red,
onPressed: () {},
),
NButton.tonal(
title: "NButton.tonal",
// primary: Colors.red,
// border: Border.all(color: Colors.transparent),
onPressed: () {},
),
NButton.tonal(
title: "NButton.tonal",
primary: Colors.red,
// border: Border.all(color: Colors.transparent),
onPressed: () {},
),
NButton.tonal(
primary: Colors.white,
title: "NButton.tonal",
style: TextStyle(color: Colors.black87),
border: Border.all(color: Color(0xffe4e4e4)),
onPressed: () {},
),
NButton.text(
// primary: Colors.red,
title: "NButton.text",
onPressed: () {},
),
]
.map((e) => Container(
width: 180,
padding: const EdgeInsets.all(8.0),
child: e,
))
.toList(),
NButton.text(
// primary: Colors.red,
width: 26,
child: Icon(Icons.arrow_back_ios_new, color: Colors.blue),
onPressed: () {},
),
]),
),
三、Button 源码
1、NButtonTheme 源码
/// 确定按钮 NButton
class NButtonTheme extends ThemeExtension<NButtonTheme> {
/// 确定按钮
const NButtonTheme({
this.primary,
this.height,
this.margin,
this.padding,
this.gradient,
this.gradientDisable,
this.boxShadow,
this.boxShadowDisable,
this.border,
this.borderDisable,
this.borderRadius,
this.borderRadiusDisable,
this.textStyle,
this.textStyleDisable,
});
final Color? primary;
/// 点击区域高
final double? height;
/// 点击区域外边距
final EdgeInsets? margin;
/// 点击区域内边距
final EdgeInsets? padding;
/// 点击区域背景颜色(渐进)
final Gradient? gradient;
final Gradient? gradientDisable;
/// 阴影
final List<BoxShadow>? boxShadow;
/// 阴影
final List<BoxShadow>? boxShadowDisable;
final Border? border;
final Border? borderDisable;
final BorderRadius? borderRadius;
final BorderRadius? borderRadiusDisable;
/// 文字颜色
final TextStyle? textStyle;
final TextStyle? textStyleDisable;
@override
ThemeExtension<NButtonTheme> copyWith({
Color? primary,
double? height,
EdgeInsets? margin,
EdgeInsets? padding,
Gradient? gradient,
Gradient? gradientDisable,
List<BoxShadow>? boxShadow,
List<BoxShadow>? boxShadowDisable,
BorderRadius? borderRadius,
BorderRadius? borderRadiusDisable,
Border? border,
Border? borderDisable,
}) =>
NButtonTheme(
primary: primary ?? this.primary,
height: height ?? this.height,
margin: margin ?? this.margin,
padding: padding ?? this.padding,
gradient: gradient ?? this.gradient,
gradientDisable: gradientDisable ?? this.gradientDisable,
boxShadow: boxShadow ?? this.boxShadow,
boxShadowDisable: boxShadowDisable ?? this.boxShadowDisable,
borderRadius: borderRadius ?? this.borderRadius,
borderRadiusDisable: borderRadiusDisable ?? this.borderRadiusDisable,
border: border ?? this.border,
borderDisable: borderDisable ?? this.borderDisable,
);
@override
ThemeExtension<NButtonTheme> lerp(
covariant NButtonTheme? other,
double t,
) =>
NButtonTheme(
primary: Color.lerp(primary, other?.primary, t),
margin: EdgeInsets.lerp(margin, other?.margin, t),
padding: EdgeInsets.lerp(padding, other?.padding, t),
gradient: Gradient.lerp(gradient, other?.gradient, t),
gradientDisable:
Gradient.lerp(gradientDisable, other?.gradientDisable, t),
borderRadius: BorderRadius.lerp(borderRadius, other?.borderRadius, t),
borderRadiusDisable: BorderRadius.lerp(
borderRadiusDisable, other?.borderRadiusDisable, t),
border: Border.lerp(border, other?.border, t),
borderDisable: Border.lerp(borderDisable, other?.borderDisable, t),
);
}
2、NButton 源码
//
// NButton.dart
// flutter_templet_project
//
// Created by shang on 2023/12/28 11:56.
// Copyright © 2023/12/28 shang. All rights reserved.
//
import 'package:flutter/material.dart';
import 'package:flutter_templet_project/basicWidget/n_text.dart';
import 'package:flutter_templet_project/basicWidget/theme/n_button_theme.dart';
import 'package:flutter_templet_project/extension/build_context_ext.dart';
import 'package:flutter_templet_project/extension/ddlog.dart';
/// 主题色背景确定按钮
class NButton extends StatelessWidget {
const NButton({
super.key,
this.primary,
this.enable = true,
this.title = '确定',
this.style,
this.width,
this.height,
this.margin,
this.padding,
this.borderRadius,
this.gradient,
this.boxShadow,
this.border,
required this.onPressed,
this.onLongPressed,
this.onTitle,
this.child,
});
factory NButton.tonal({
Key? key,
Color? primary,
String title,
TextStyle? style,
bool enable,
double? width,
double? height,
EdgeInsets? margin,
EdgeInsets? padding,
BorderRadius? borderRadius,
Border? border,
VoidCallback? onLongPressed,
required VoidCallback? onPressed,
ValueChanged<String>? onTitle,
Widget? child,
}) = _NButtonTonal;
factory NButton.text({
Key? key,
Color? primary,
String title,
TextStyle? style,
bool enable,
double? width,
double? height,
EdgeInsets? margin,
EdgeInsets? padding,
BorderRadius? borderRadius,
Border? border,
VoidCallback? onLongPressed,
required VoidCallback? onPressed,
ValueChanged<String>? onTitle,
Widget? child,
}) = _NButtonText;
/// 主题色
final Color? primary;
/// 标题
final String title;
/// 标题样式
final TextStyle? style;
/// 按钮是否可点击
final bool enable;
/// 高度
final double? height;
/// 宽度
final double? width;
/// 外边距
final EdgeInsets? margin;
/// 内边距
final EdgeInsets? padding;
/// 渐进色背景
final Gradient? gradient;
/// 阴影
final List<BoxShadow>? boxShadow;
/// 圆角
final BorderRadius? borderRadius;
/// 边框线
final Border? border;
/// 点击事件
final VoidCallback? onPressed;
/// 长按事件
final VoidCallback? onLongPressed;
/// 标题回调
final ValueChanged<String>? onTitle;
final Widget? child;
@override
Widget build(BuildContext context) {
final theTheme = Theme.of(context).extension<NButtonTheme>();
final primaryNew = primary ?? theTheme?.primary ?? context.primaryColor;
final heightNew = height ?? theTheme?.height ?? 44;
final marginNew = margin ?? theTheme?.margin;
final paddingNew = padding ?? theTheme?.padding;
final gradientNew = gradient ??
theTheme?.gradient ??
LinearGradient(
colors: [primaryNew, primaryNew],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
);
final gradientDisableNew = theTheme?.gradientDisable ??
LinearGradient(
colors: [
const Color(0xffF3F3F3),
const Color(0xffF3F3F3),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
);
final boxShadowNew = boxShadow ?? theTheme?.boxShadow;
final boxShadowDisableNew = theTheme?.boxShadowDisable;
final borderRadiusNew =
theTheme?.borderRadius ?? const BorderRadius.all(Radius.circular(8));
final borderRadiusDisableNew = theTheme?.borderRadiusDisable ??
const BorderRadius.all(Radius.circular(8));
final borderNew = theTheme?.border;
final borderDisableNew = theTheme?.borderDisable;
final decorationEnable = BoxDecoration(
// color: Colors.transparent,
border: borderNew,
borderRadius: borderRadiusNew,
gradient: gradientNew,
boxShadow: boxShadowNew,
);
final decorationDisable = BoxDecoration(
// color: Colors.transparent,
border: borderDisableNew,
borderRadius: borderRadiusDisableNew,
gradient: gradientDisableNew,
boxShadow: boxShadowDisableNew,
);
final decoration = enable ? decorationEnable : decorationDisable;
final textColor = enable ? Colors.white : Color(0xffb3b3b3);
return GestureDetector(
onTap: () {
if (!enable) {
DLog.d("按钮禁用");
return;
}
if (onTitle != null) {
onTitle?.call(title);
return;
}
onPressed?.call();
},
onLongPress: onLongPressed,
child: Container(
width: width,
height: heightNew,
margin: marginNew,
padding: paddingNew,
alignment: Alignment.center,
decoration: decoration,
child: child ??
NText(
title,
color: textColor,
fontSize: 16,
maxLines: 1,
style: style ?? theTheme?.textStyle,
),
),
);
}
}
/// 主题浅色背景
class _NButtonTonal extends NButton {
const _NButtonTonal({
super.key,
super.primary,
super.enable,
super.title = '确定',
super.style,
super.width,
super.height,
super.margin,
super.padding,
super.gradient,
super.borderRadius,
super.border,
required super.onPressed,
super.onLongPressed,
super.onTitle,
super.child,
});
@override
Widget build(BuildContext context) {
if (!enable) {
return NButton(
key: key,
enable: enable,
title: title,
style: style,
width: width,
height: height,
margin: margin,
padding: padding,
gradient: gradient,
borderRadius: borderRadius,
border: border,
onPressed: onPressed,
onLongPressed: onLongPressed,
onTitle: onTitle,
child: child,
);
}
final theTheme = Theme.of(context).extension<NButtonTheme>();
final primaryNew = primary ?? theTheme?.primary ?? context.primaryColor;
final heightNew = height ?? theTheme?.height ?? 44;
final marginNew = margin ?? theTheme?.margin;
final paddingNew = padding ?? theTheme?.padding;
final decoration = BoxDecoration(
color: primaryNew.withOpacity(0.1),
border: border ?? theTheme?.border ?? Border.all(color: primaryNew),
borderRadius:
theTheme?.borderRadius ?? const BorderRadius.all(Radius.circular(8)),
);
final textColor = primaryNew;
return GestureDetector(
onTap: () {
if (onTitle != null) {
onTitle?.call(title);
return;
}
onPressed?.call();
},
onLongPress: onLongPressed,
child: Container(
width: width,
height: heightNew,
margin: marginNew,
padding: paddingNew,
alignment: Alignment.center,
decoration: decoration,
child: child ??
NText(
title,
color: textColor,
fontSize: 16,
maxLines: 1,
style: style ?? theTheme?.textStyle,
),
),
);
}
}
/// 纯文字
class _NButtonText extends NButton {
const _NButtonText({
super.key,
super.primary,
super.enable,
super.title = '确定',
super.style,
super.width,
super.height,
super.margin,
super.padding,
super.borderRadius,
super.border,
required super.onPressed,
super.onLongPressed,
super.onTitle,
super.child,
});
@override
Widget build(BuildContext context) {
if (!enable) {
return NButton(
key: key,
enable: enable,
title: title,
style: style,
width: width,
height: height,
margin: margin,
padding: padding,
gradient: gradient,
borderRadius: borderRadius,
border: border,
onPressed: onPressed,
onLongPressed: onLongPressed,
onTitle: onTitle,
child: child,
);
}
final theTheme = Theme.of(context).extension<NButtonTheme>();
final primaryNew = primary ?? theTheme?.primary ?? context.primaryColor;
final heightNew = height ?? theTheme?.height;
final marginNew = margin ?? theTheme?.margin;
final paddingNew = padding ?? theTheme?.padding;
final decoration = BoxDecoration(
// color: primaryNew.withOpacity(0.1),
border:
border ?? theTheme?.border ?? Border.all(color: Colors.transparent),
borderRadius:
theTheme?.borderRadius ?? const BorderRadius.all(Radius.circular(8)),
);
final textColor = primaryNew;
return GestureDetector(
onTap: () {
if (onTitle != null) {
onTitle?.call(title);
return;
}
onPressed?.call();
},
onLongPress: onLongPressed,
child: Container(
width: width,
height: heightNew,
margin: marginNew,
padding: paddingNew,
alignment: Alignment.center,
decoration: decoration,
child: child ??
NText(
title,
color: textColor,
fontSize: 16,
maxLines: 1,
style: style ?? theTheme?.textStyle,
),
),
);
}
}
最后、总结
1、通过 NButton 及子类 _NButtonTonal、_NButtonText 实现,可以满足常见效果配置。再通过 NButtonTheme 主题配置,三种样式颜色,阴影,渐变色可统一设置。极大的提高了开发效率!
2、程序的世界是一通百通,其他组件的多样式封装也就不是问题了。