App
Warning
App not implement any chobits feature right now,just make a basic framework.
Framework
- Theme framework
- Adaptive
- Desktop or not
- Text scale
- I18n
- Network
- Dio
- Database
- Sqlite
- Util
- Unique Id
- nanoid2
- Unique Id
- Event Bus
- Auto upgrade
- Android
- Logging
- Rotating log File(Without Web Env)
- Export log file or upload log file
- Release
- Android
- IOS
- Windows
- exe(unpack)
- Linux
- MacOS
- Web
- Env Config
- Dev
- Prod
Advance
- Auth
- Spring-authorization-server
- User Profile
Develop Flow
- Changelog
- CI
- Build
- Code Quality
- Test
- Testing
- Unit Test(Example)
- Widget Test(Example)
- Integration Test
Coding
Database Versioning
lib\modules\app\app_store.dart
//DB init
DbManager.instance().init([ChangeLogV1()]);
Database db = await DbManager.instance().open();
Database Record To View Model
Example
class MemoMapper{
static Future<List<MemoEntity>> selectAll() async {
List<Map<String, dynamic>> findResult =
await DbManager.instance().find(MemoEntity.tableName);
return Future.value(findResult.map((e) => MemoEntity.fromJson(e)).toList());
}
}
class AppStore{
Future<List<MemoModel>> getMemoList() async {
List<MemoEntity> memoEntityList = await MemoMapper.selectAll();
return Future(() =>
memoEntityList.map((e) => MemoModel.fromJson(e.toJson())).toList());
}
}
class _MemoPageState{
void initState() {
super.initState();
Provider.of<AppStore>(context, listen: false).getMemoList().then((value) {
setState(() {
memoModelList = value;
});
});
}
}
Pagging(Pull refresh and load more)
Example
class _MemoPageState extends State<MemoPage> {
List<MemoModel> _memoModelList = [];
late EasyRefreshController _controller;
late int _pageNum;
late int _pageSize;
late int _total;
@override
void initState() {
super.initState();
resetPageInfo();
_controller = EasyRefreshController(
controlFinishRefresh: true,
controlFinishLoad: true,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void resetPageInfo() {
setState(() {
_pageNum = 1;
_pageSize = 10;
_total = 0;
_memoModelList = [];
});
}
Future<PageResult> _loadData() async {
var result = await Provider.of<AppStore>(context, listen: false).pageMemo(
PageParam(
pageNum: _pageNum, pageSize: _pageSize, orderBy: " datetime desc"));
if (!mounted) {
return Future.value(result);
}
_memoModelList.addAll(result.rows);
_total = result.total;
LogHelper.debug(
"[Memo] loadData pageNum = $_pageNum,pageSize = $_pageSize,total = $_total");
return Future.value(result);
}
void _refreshList() async {
resetPageInfo();
await _loadData();
setState(() {});
_controller.finishRefresh();
_controller.resetFooter();
}
void _loadList() async {
_pageNum++;
PageResult result = await _loadData();
setState(() {});
if (result.hasNext) {
_controller.finishLoad(IndicatorResult.success);
} else {
_controller.finishLoad(IndicatorResult.noMore);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: EasyRefresh(
refreshOnStart: true,
controller: _controller,
header: RefreshHeader(context),
footer: RefreshFooter(context),
onRefresh: _refreshList,
onLoad: _loadList,
child: CustomScrollView(slivers: [
SliverGrid(
delegate: SliverChildBuilderDelegate((context, index) {
return _createMemoItem(_memoModelList[index]);
}, childCount: _memoModelList.length),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 210),
)
])),
floatingActionButton: _getFloatingActionButton());
}
}
HttpRequest
Response result = await HttpClient.instance().get("/");
LogHelper.info(result.body().toString());
JSON Model Gen
dart run build_runner build
Release
Android
-
Create an keystore
keytool -genkey -v -keystore .\android-app-keystore.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias android-app -
create [project]/android/key.properties and reference the keystore from the app
storePassword=<password from previous step> keyPassword=<password from previous step> keyAlias=android-app storeFile=<location of the key store file, such as /Users/<user name>/android-app-keystore.jks or C:\\Users\\<user name>\\android-app-keystore.jks> -
run release command(eg: prod env)
flutter build apk --dart-define=DART_DEFINE_APP_ENV=prod
Conventional Commits
https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format
The Conventional Commits specification is a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of. This convention dovetails with SemVer, by describing the features, fixes, and breaking changes made in commit messages.
The commit message should be structured as follows:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
The commit contains the following structural elements, to communicate intent to the consumers of your library:
- fix: a commit of the type
fixpatches a bug in your codebase (this correlates withPATCHin Semantic Versioning). - feat: a commit of the type
featintroduces a new feature to the codebase (this correlates withMINORin Semantic Versioning). - BREAKING CHANGE: a commit that has a footer
BREAKING CHANGE:, or appends a!after the type/scope, introduces a breaking API change (correlating withMAJORin Semantic Versioning). A BREAKING CHANGE can be part of commits of any type. - types other than
fix:andfeat:are allowed, for example @commitlint/config-conventional (based on the Angular convention) recommendsbuild:,chore:,ci:,docs:,style:,refactor:,perf:,test:, and others. - footers other than
BREAKING CHANGE: <description>may be provided and follow a convention similar to git trailer format.
Additional types are not mandated by the Conventional Commits specification, and have no implicit effect in Semantic Versioning (unless they include a BREAKING CHANGE). A scope may be provided to a commit’s type, to provide additional contextual information and is contained within parenthesis, e.g., feat(parser): add ability to parse arrays.
Sqlite3 on web
sqflite_common_ffi_web
Setup binaries
Implementation requires sqlite3.wasm binaries into your web folder as well as a sqflite specific shared worker.
You can install binaries using the command:
dart run sqflite_common_ffi_web:setup
It should create the following files in your web folder:
sqlite3.wasmsqflite_sw.js
that you can put in source control or not (personally I don’t)
Note: when sqlite3 and its wasm binary are updated, you may need to run the command again using the force option:
dart run sqflite_common_ffi_web:setup --force
Offline Map
-
Generate XYZ tiles(Directory)
QGIS->Toolbox->Generate XYZ tiles(Directory)
-
Gen pubspec.yaml assets path
ls -R assets | grep ':'
Coordinate
-
Server save the coordinate format is WGS84
-
Client map is gaode map,who’s format is GCJ-02;
-
So we must transform the coordinate from WGS84 to GCJ-02 and we use the util coordtransform_dart.
Q&A
Q1. setState() or markNeedsBuild() called during build. This ModelBinding widget cannot be marked as needing to build because the framework is already in the process of building widgets.
Solution 1: use a call back function You just need to use a call back function. Because Should be setState method call before the build method had completed the process of building the widgets and thats why you are facing this error.
WidgetsBinding.instance.addPostFrameCallback((_){
// Your Code Here
});
Other
- pod install slow
cd ./ios/
#如有clash这类代理软件,则执行下面代理设置命令,使用代理进行依赖库的下载
#export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
pod install --verbose