# flutter_htmlful **Repository Path**: zoar/flutter_htmlful ## Basic Information - **Project Name**: flutter_htmlful - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-05 - **Last Updated**: 2024-10-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 本库主要希望在 flutter 中可以以 html 的风格去写布局,以此简化布局和减少flutter中的布局嵌套。 和html一样把布局概念分为三部分:元素、样式、事件。 所有组件其实都是用flutter的原用组件组合而来,我们不创造新的组件,所以flutter支持的特性基本都是支持的。 ## Features TODO: List what your package can do. Maybe include images, gifs, or videos. ## Getting started ``` dependencies: flutter_htmlful: git: url: https://gitee.com/zoar/flutter_htmlful.git ref: main ``` ## Usage 先看实际用例 效果图 ![img_1.png](img_1.png) 代码 ```dart import 'package:demo/bloc/home_mine_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_htmlful/flutter_htmlful.dart'; class HomeMinePage extends StatefulWidget { const HomeMinePage({super.key}); @override State createState() => _HomeMinePageState(); } class _HomeMinePageState extends State { @override Widget build(BuildContext context) { return BlocProvider( create: (context) => HomeMineCubit(), child: BlocBuilder( builder: (BuildContext context, HomeMineState state) { return Div( style: Style( width: double.infinity, height: double.infinity, backgroundColor: Colors.red, backgroundGradient: const LinearGradient( colors: [ Color(0xff65c3aa), Color(0xff96c0aa), Color(0xfffffeaa), Color(0xffEEEFaa), Color(0xffEDEEaa), Color(0xffECEEaa), Color(0xfff2f2f2), ], stops: [0, 0.08, 0.20, 0.28, 0.30, 0.5, 1], begin: Alignment.topLeft, end: Alignment.bottomRight, ), scrollable: true, ), body: SafeArea( top: true, child: Div( body: [ Div( body: [ Img( src: 'https://cdn.pixabay.com/photo/2024/03/04/16/07/winter-8612635_1280.jpg', errorSrc: 'assets/images/img.png', style: Style( height: 56, width: 56, fit: BoxFit.fill, radius: 1000, ), inkWell: InkWell( onTap: () { debugPrint('Img'); }, ), ), Div( body: [ const Text( 'xxxxxxx', style: TextStyle(fontSize: 16), ), Div( body: const Text( 'xxxxxxxy', style: TextStyle( fontSize: 12, color: Color(0xffFCE305), ), ), style: Style( backgroundColor: Colors.black, paddingLeft: 10, paddingRight: 10, paddingBottom: 1.5, radius: 1000, marginTop: 4, ), ) ], style: Style(marginLeft: 12, flexGrow: 1), ), Div( body: const [ Icon( Icons.settings_rounded, size: 20, ), Text( '设置', style: TextStyle( color: Color(0xff333333), fontSize: 12, ), ), ], style: Style(crossAxisAlignment: CrossAxisAlignment.center), ), Div( body: const [ Icon( Icons.account_balance_sharp, size: 20, ), Text( '切换', style: TextStyle( color: Color(0xff333333), fontSize: 12, ), ), ], style: Style(marginLeft: 20, crossAxisAlignment: CrossAxisAlignment.center), ) ], style: Style( direction: Axis.horizontal, crossAxisAlignment: CrossAxisAlignment.center, radius: 10, margin: 10, ), ), Div( body: [ Div( body: [ const Text( '统计', style: TextStyle(fontSize: 14), ), Div( body: const [ Text( '查看全部记录', style: TextStyle( fontSize: 12, color: Colors.white, height: 1, ), ), Icon( Icons.chevron_right, size: 16, color: Colors.white, ) ], style: Style( crossAxisAlignment: CrossAxisAlignment.center, backgroundColor: const Color(0xff62bdc9), // backgroundGradient: const LinearGradient( // colors: [Color(0xff41A4D9), Color(0xff41A4D9), Color(0xff40EBD4)], // begin: Alignment.centerLeft, // end: Alignment.centerRight, // ), direction: Axis.horizontal, paddingLeft: 8, paddingRight: 6, paddingBottom: 4, paddingTop: 4, radius: 20, ), inkWell: InkWell( onTap: () { debugPrint('查看全部记录'); }, ), ), ], style: Style( direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, ), ), Div( body: [1, 2, 3, 4, 5, 6] .map( (e) => Div( body: [ Text( 'text $e', style: const TextStyle(fontSize: 16), ), ], style: Style( backgroundColor: Colors.black12, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, radius: 100, height: 40 + e * 1, width: 70 + e * 1, ), ), ) .toList(), style: Style( width: double.infinity, direction: Axis.horizontal, alignment: WrapAlignment.spaceBetween, marginTop: 12, wrap: true, spacing: 10, runSpacing: 10, ), ) ], style: Style( backgroundColor: Colors.white, radius: 12, marginTop: 24, padding: 12, marginLeft: 10, marginRight: 10, ), ), Div( body: [ Div( body: const Text( '功能模块', style: TextStyle(fontSize: 14), ), ), Div( body: [ Div( body: [ Img( src: 'assets/images/img.png', style: Style( height: 24, width: 24, fit: BoxFit.fill, marginBottom: 4, ), ), const Text( '模块1', style: TextStyle( fontSize: 12, ), ) ], style: Style( crossAxisAlignment: CrossAxisAlignment.center, border: Border.all( color: const Color(0xffDEDEDE), width: 1, ), radius: 6, paddingTop: 10, paddingBottom: 10, flexGrow: 1, ), ), const VerticalDivider(width: 12), Div( body: [ Img( src: 'assets/images/img.png', style: Style( height: 24, width: 24, fit: BoxFit.fill, marginBottom: 4, ), ), const Text( '模块2', style: TextStyle( fontSize: 12, ), ) ], style: Style( crossAxisAlignment: CrossAxisAlignment.center, border: Border.all( color: const Color(0xffDEDEDE), width: 1, ), radius: 6, paddingTop: 10, paddingBottom: 10, flexGrow: 1, ), ), const VerticalDivider(width: 12), Div( body: [ Img( src: 'assets/images/img.png', style: Style( height: 24, width: 24, fit: BoxFit.fill, marginBottom: 4, ), ), const Text( '模块3', style: TextStyle( fontSize: 12, ), ) ], style: Style( crossAxisAlignment: CrossAxisAlignment.center, border: Border.all( color: const Color(0xffDEDEDE), width: 1, ), radius: 6, paddingTop: 10, paddingBottom: 10, flexGrow: 1, ), inkWell: InkWell( onTap: () { debugPrint('onTap 11111'); }, ), ), ], style: Style( direction: Axis.horizontal, crossAxisAlignment: CrossAxisAlignment.center, marginTop: 12, ), ) ], style: Style( backgroundColor: Colors.white, radius: 12, marginTop: 12, padding: 12, marginLeft: 10, marginRight: 10, ), ), Div( body: state.projects .map( (e) => Div( body: [ const Icon(Icons.ac_unit), Div( body: Text('${e.url}'), style: Style(marginLeft: 12, flexGrow: 1), ), const Icon(Icons.chevron_right, color: Color(0xff999999)), ], style: Style( direction: Axis.horizontal, crossAxisAlignment: CrossAxisAlignment.center, padding: 12, ), inkWell: InkWell( onTap: () { // context.read().changeNavigationBarIndex(0); context.read().requestData(); }, ), ), ) .toList(), style: Style( margin: 10, backgroundColor: Colors.white, radius: 12, ), ), Div( body: const [ Text( '退出', style: TextStyle(color: Colors.white, fontSize: 16), ) ], style: Style( padding: 10, margin: 10, mainAxisAlignment: MainAxisAlignment.center, direction: Axis.horizontal, radius: 1000, backgroundGradient: const LinearGradient( colors: [Color(0xff41A4D9), Color(0xff41A4D9), Color(0xff40EBD4)], begin: Alignment.centerLeft, end: Alignment.centerRight, ), ), inkWell: InkWell( onTap: () { debugPrint('退出'); }, ), ) ], ), ), ); }, ), ); } } ``` - style支持的属性如下: ```dart import 'package:flutter/cupertino.dart'; class Style { double? width; double? height; ///背景色;如果设置了 backgroundGradient 或 backgroundImage 将会无效 Color? backgroundColor; Gradient? backgroundGradient; BlendMode? backgroundBlendMode; ImageProvider? backgroundImage; BoxFit? fit; List? boxShadow; BoxBorder? border; ///是否可以滚动,滚动方向同 direction bool scrollable; ///超出布局部分的处理 Clip clipBehavior; double radius; double radiusTopLeft; double radiusTopRight; double radiusBottomLeft; double radiusBottomRight; double margin; double marginTop; double marginLeft; double marginRight; double marginBottom; double padding; double paddingTop; double paddingLeft; double paddingRight; double paddingBottom; /// 布局方向,默认垂直:Axis.vertical Axis direction; ///flex布局才生效的相关属性 MainAxisAlignment mainAxisAlignment; MainAxisSize mainAxisSize; CrossAxisAlignment crossAxisAlignment; TextDirection? textDirection; VerticalDirection verticalDirection; TextBaseline? textBaseline; ///同 flex 布局的 Expanded,占据剩余空间的比例,Div 的 body 为数组时有效 int flexGrow; ///是否为流式布局,以及流式布局才生效的相关属性 bool wrap; double spacing; double runSpacing; WrapAlignment alignment; WrapAlignment runAlignment; WrapCrossAlignment wrapCrossAlignment; Style({ this.height, this.width, this.backgroundColor, this.backgroundGradient, this.backgroundBlendMode, this.backgroundImage, this.fit, this.boxShadow, this.border, this.scrollable = false, this.clipBehavior = Clip.none, this.radius = 0, this.radiusBottomLeft = 0, this.radiusBottomRight = 0, this.radiusTopLeft = 0, this.radiusTopRight = 0, this.margin = 0, this.marginTop = 0, this.marginLeft = 0, this.marginBottom = 0, this.marginRight = 0, this.padding = 0, this.paddingBottom = 0, this.paddingLeft = 0, this.paddingRight = 0, this.paddingTop = 0, this.direction = Axis.vertical, this.mainAxisAlignment = MainAxisAlignment.start, this.mainAxisSize = MainAxisSize.max, this.crossAxisAlignment = CrossAxisAlignment.start, this.textDirection, this.verticalDirection = VerticalDirection.down, this.textBaseline, this.flexGrow = 0, this.wrap = false, this.spacing = 0, this.runSpacing = 0, this.alignment = WrapAlignment.start, this.runAlignment = WrapAlignment.start, this.wrapCrossAlignment = WrapCrossAlignment.start, }); } ``` - 事件支持如下(同InkWell): ```dart InkWell( onTap, onLongPress, onSecondaryTap, ) ``` - Div组件基本使用如下: ```dart Div( body: const [ Text( '退出', style: TextStyle(color: Colors.white, fontSize: 16), ) ], style: Style( padding: 10, margin: 10, mainAxisAlignment: MainAxisAlignment.center, direction: Axis.horizontal, radius: 1000, backgroundGradient: const LinearGradient( colors: [Color(0xff41A4D9), Color(0xff41A4D9), Color(0xff40EBD4)], begin: Alignment.centerLeft, end: Alignment.centerRight, ), ), inkWell: InkWell( onTap: () { debugPrint('退出'); }, ), ) ``` 1、body可以是数组也可以是单个组件 2、需要子组件居中时,最好body为数组。水平居中时direction设置成Axis.horizontal,这样很多时候就可以不用设置宽度,更方便(如用例),这是由flutter自己的特性决定的。 3、backgroundColor设置后,不要设置backgroundGradient等,否则会被覆盖(同flutter) - Img组件使用如下: ```dart Img( src: 'https://cdn.pixabay.com/photo/2024/03/04/16/07/winter-8612635_1280.jpg', errorSrc: 'assets/images/img.png', style: Style( height: 56, width: 56, fit: BoxFit.fill, radius: 1000, ), inkWell: InkWell( onTap: () { debugPrint('Img'); }, ), ) ``` ## Additional information TODO: Tell users more about the package: where to find more information, how to contribute to the package, how to file issues, what response they can expect from the package authors, and more.