Commit 5323925c authored by 汪林玲's avatar 汪林玲

Initial commit

parents
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: f7a6a7906be96d2288f5d63a5a54c515a6e987fe
channel: unknown
project_type: package
## [0.0.1] - TODO: Add release date.
* TODO: Describe initial release.
TODO: Add your license here.
# navigation_bar
通用NavigationBar
## DEMO
```
Container( // 必须使用确定高度的Widget包裹起来
height: 35,
child: NavigationBar(
selectColor: Colors.green, // 选中字体颜色
normalColor: Colors.red, // 未选中字体颜色
controller: tabController, // 控制器
selectStyle: TextStyle(fontWeight: FontWeight.bold), // 选中字体样式,color无效
items: ["TAB_1", "TAB_2", "TAB_3"], // 数据列表
isScrollable: true, // 是否可以滚动,false时item平分tabbar宽度
indicatorSize: TabBarIndicatorSize.tab, // 指示器宽度,tab占满item,label对齐item文字宽度,对ImageIndicator和设置了宽度RoundRectIndicator无效
indicator: ImageIndicator( // 指示器
image: 'assets/test/home_choose_logo.png',
width: 12,
height: 6,
marginBottom: 3,
color: Colors.green
),
onChange: (index) { // index改变事件
}
),
)
```
## 指示器
```
ImageIndicator( // 图片指示器
image: 'assets/test/home_choose_logo.png', // 图片地址,仅支持本地
width: 12, // 图片显示宽度
height: 6, // 图片显示高度
marginBottom: 3, // 图片距离底部边距
color: Colors.green, // 颜色,可以改变图片颜色
)
RoundRectIndicator( // 圆角矩形指示器
round: 2, // 指示器圆角
width: 30, // 显示宽度
height: 2, // 显示高度
marginBottom: 3, // 距离底部边距
color: Colors.green, // 颜色
)
```
This diff is collapsed.
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
enum IndicatorAxisAlignment {
start,
end,
center,
}
class ImageIndicator extends Decoration {
final ui.Image? image;
final Color? color;
final double? width;
final double? height;
final double? marginBottom;
final double? marginRight;
final IndicatorAxisAlignment alignment;
ImageIndicator(
{required this.image,
this.color,
this.height,
this.width,
this.alignment = IndicatorAxisAlignment.center,
this.marginBottom = 0,
this.marginRight = 0});
@override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
return _ImageIndicatorPaint(dec: this, onChanged:onChanged);
}
}
class RoundRectIndicator extends Decoration {
final Color? color;
final double? width;
final double? height;
final double round;
final double? marginBottom;
final double? marginRight;
RoundRectIndicator(
{required this.color,
this.height = 2,
this.width,
this.round = 1,
this.marginBottom = 0,
this.marginRight=0});
@override
BoxPainter createBoxPainter([void Function()? onChanged]) {
return _RoundRectPainter(dec: this, onChanged:onChanged);
}
}
class _ImageIndicatorPaint extends BoxPainter {
final ImageIndicator dec;
final VoidCallback? onChanged;
late Paint painter;
_ImageIndicatorPaint({required this.dec,required this.onChanged}) : super() {
painter = Paint();
painter.style = PaintingStyle.stroke;
painter.isAntiAlias = true;
if (dec.color != null) {
painter.colorFilter = ColorFilter.mode(dec.color!, BlendMode.srcIn);
}
}
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration config) async {
double? width = dec.width;
double? height = dec.height;
Size iSize = config.size!;
if (width == null || height == null) {
width = dec.image!.width.toDouble();
height = dec.image!.height.toDouble();
}
// 计算图片绘制中心坐标
double xAxis = offset.dx;
double yAxis = offset.dy + iSize.height - height / 2 - dec.marginBottom!;
if (dec.alignment == IndicatorAxisAlignment.center) {
xAxis = offset.dx + iSize.width / 2;
} else if (dec.alignment == IndicatorAxisAlignment.end) {
xAxis = offset.dx + iSize.width + dec.marginRight!;
}
Offset centerOffset = Offset(xAxis, yAxis);
Rect rect =
Rect.fromCenter(center: centerOffset, width: width, height: height);
canvas.drawImageNine(dec.image!, rect, rect, painter);
}
}
class _RoundRectPainter extends BoxPainter {
final RoundRectIndicator dec;
final VoidCallback? onChanged;
late Paint painter;
_RoundRectPainter({required this.dec,required this.onChanged}) : super() {
painter = Paint();
painter.style = PaintingStyle.fill;
painter.isAntiAlias = true;
if (dec.color != null) {
painter.colorFilter = ColorFilter.mode(dec.color!, BlendMode.srcIn);
}
}
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration config) async {
Rect rect = _indicatorRectFor(offset & config.size!, config.textDirection!);
RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(dec.round));
canvas.drawRRect(rRect, painter);
}
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
Rect indicator = EdgeInsets.zero.resolve(textDirection).deflateRect(rect);
double? width = dec.width;
double left = indicator.left;
if (width == null) {
width = indicator.width;
} else {
left += (indicator.width - width) / 2;
}
return Rect.fromLTWH(
left,
indicator.bottom - dec.height! - dec.marginBottom!,
width,
dec.height!,
);
}
}
library navigation_bar;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'indicator.dart' as indicator;
import './i_tabbar.dart';
export 'indicator.dart' show IndicatorAxisAlignment;
class NavigationBar extends StatefulWidget {
final List<String>? items;
final double? labelWidth;
final ItemBuilderDelegate? builderDelegate;
final TabController? controller;
final Color selectColor;
final Color normalColor;
final TextStyle? selectStyle;
final TextStyle? normalStyle;
final _Indicator? indicator;
final bool isScrollable;
final TabBarIndicatorSize indicatorSize;
final EdgeInsetsGeometry? labelPadding;
final Function(int? index)? onChange;
final Function(int)? onTap;
NavigationBar(
{this.items,
this.labelWidth,
this.builderDelegate,
this.controller,
this.selectStyle,
this.normalStyle,
this.indicator,
this.onChange,
this.onTap,
this.labelPadding,
this.isScrollable = false,
this.selectColor = Colors.black,
this.normalColor = Colors.black12,
this.indicatorSize = TabBarIndicatorSize.label,
Key? key})
: super(key: key) {
assert(items != null || builderDelegate != null, '列表项不能为空');
}
@override
State<StatefulWidget> createState() => NavigationBarState();
}
class NavigationBarState extends State<NavigationBar> {
int? _index;
TabController? _oldController;
Future<ui.Image?> get indicatorImage async {
if (isImageIndicator) {
ImageIndicator indicator = widget.indicator as ImageIndicator;
var image = await loadImage(indicator.image);
return image;
}
return null;
}
Future<Decoration?> get indicatorDecoration async {
if (isImageIndicator) {
ImageIndicator imageIndicator = widget.indicator as ImageIndicator;
return indicator.ImageIndicator(
image: await indicatorImage,
color: imageIndicator.color,
width: imageIndicator.width,
height: imageIndicator.height,
alignment: imageIndicator.alignment,
marginBottom: imageIndicator.marginBottom,
marginRight: imageIndicator.marginRight);
} else {
RoundRectIndicator rectIndicator = widget.indicator as RoundRectIndicator;
return indicator.RoundRectIndicator(
color: rectIndicator.color,
width: rectIndicator.width,
height: rectIndicator.height,
round: rectIndicator.round,
marginBottom: rectIndicator.marginBottom,
marginRight: rectIndicator.marginRight,
);
}
}
bool get isImageIndicator {
return widget.indicator is ImageIndicator;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
initEvnet();
}
@override
void didUpdateWidget(covariant NavigationBar oldWidget) {
super.didUpdateWidget(oldWidget);
initEvnet();
}
@override
void dispose() {
if (_oldController != null) {
_oldController!.removeListener(listenerCallback);
}
super.dispose();
}
void initEvnet() {
if (_oldController != _getTabController()) {
_oldController = _getTabController();
_getTabController()!.removeListener(listenerCallback);
_getTabController()!.addListener(listenerCallback);
}
}
void listenerCallback() {
if (_index == _getTabController()!.index) {
return;
}
_index = _getTabController()!.index;
if (widget.onChange != null) {
widget.onChange!(_index);
}
}
TabController? _getTabController() {
if (widget.controller != null) {
return widget.controller;
} else {
return DefaultTabController.of(context);
}
}
@override
Widget build(BuildContext context) {
return FutureBuilder<Decoration?>(
future: indicatorDecoration,
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return SizedBox();
}
List<Widget> tabs;
if (widget.builderDelegate != null) {
tabs = List.generate(widget.builderDelegate!.childCount, (index) {
return widget.builderDelegate!.builder(context, index);
});
} else {
tabs = widget.items!.map((text) {
return Container(
width: widget.labelWidth,
height: double.infinity,
alignment: Alignment.center,
child: Text(
text,
));
}).toList();
}
return ITabBar(
tabs: tabs,
labelPadding: widget.labelPadding,
controller: widget.controller,
isScrollable: widget.isScrollable,
onTap: (index) {
if (widget.onTap != null) {
widget.onTap!(index);
}
},
labelStyle:
TextStyle.lerp(TextStyle(height: 1), widget.selectStyle, 1),
unselectedLabelStyle:
TextStyle.lerp(TextStyle(height: 1), widget.normalStyle, 1),
labelColor: widget.selectColor,
unselectedLabelColor: widget.normalColor,
indicatorSize: widget.indicatorSize,
indicator: snapshot.data,
indicatorColor: Colors.transparent,
);
});
}
Future<ui.Image> loadImage(String path) async {
var data = await rootBundle.load(path);
var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
var info = await codec.getNextFrame();
return info.image;
}
}
class _Indicator {
final Color? color;
final double? width;
final double? height;
final double? marginBottom;
final double? marginRight;
_Indicator(
{this.color,
this.height,
this.width,
this.marginBottom = 0,
this.marginRight = 0});
}
class ImageIndicator extends _Indicator {
String image;
indicator.IndicatorAxisAlignment alignment;
ImageIndicator(
{required this.image,
Color? color,
double? width,
double? height,
this.alignment = indicator.IndicatorAxisAlignment.center,
double marginBottom = 0,
double marginRight = 0})
: super(
color: color,
width: width,
height: height,
marginBottom: marginBottom,
marginRight: marginRight);
}
class RoundRectIndicator extends _Indicator {
double round;
RoundRectIndicator(
{this.round = 1,
required Color color,
double? width,
double height = 2,
double? marginBottom,
double? marginRight})
: super(
color: color,
width: width,
height: height,
marginBottom: marginBottom,
marginRight: marginRight);
}
class ItemBuilderDelegate {
final int childCount;
final Widget Function(BuildContext context, int index) builder;
ItemBuilderDelegate({required this.childCount, required this.builder});
}
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.8.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.15.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
name: navigation_bar
description: A new Flutter package.
version: 1.0.0
homepage:
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment