Flutter MVVM架构插件library_architecture_mvvm_modify的使用
Flutter MVVM架构插件library_architecture_mvvm_modify的使用
介绍
library_architecture_mvvm_modify
是一个用于在 Flutter 中实现 MVVM(Model-View-ViewModel)架构的插件。它提供了一套规则和模板来帮助开发者更好地组织和管理代码。
开始使用
示例项目
你可以查看一个简单的示例项目,以便更好地理解如何使用这个插件:
如何创建基于此架构的项目?
你可以使用 GitHub 模板来快速创建项目:
文档
在阅读文档之前,请务必先阅读整个示例项目。
架构对象
以下是架构对象的列表及其继承关系和重构规则:
class NamedUtility {}
class NamedVM {}
class DataForNamed {}
class EnumDataForNamed {}
class ModelTTNamedTTNamedTTNamedTTIterator {}
class NamedException {}
class NamedState {}
class NamedStreamWState {}
class Model {}
class ListModel {}
class NamedService {}
class ModelWrapper {}
class ListModelWrapper {}
class ModelWrapperRepository {}
class TempCacheProvider {}
class ExceptionController {}
class Result {}
class ResultWithModelWrapper {}
class ResultWithListModelsWrapper {}
class CurrentModelWIndex {}
class IDispose {}
示例代码
以下是一个完整的示例代码,展示了如何使用 library_architecture_mvvm_modify
插件。
import 'dart:convert';
import 'package:library_architecture_mvvm_modify/library_architecture_mvvm_modify.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
@immutable
final class FactoryModelWrapperRepositoryUtility {
const FactoryModelWrapperRepositoryUtility._();
static IPAddressWrapperRepository
getIPAddressWrapperRepositoryFromNamedHttpClientService(
BaseNamedHttpClientService namedHttpClientService) {
return IPAddressWrapperRepository(namedHttpClientService);
}
}
@immutable
final class KeysUrlEndpointUtility {
/* JsonipAPI */
static const String jsonipAPI = "https://jsonip.com";
static const String jsonipAPIQQProviders = "$jsonipAPI/providers";
const KeysUrlEndpointUtility._();
}
@immutable
final class ReadyDataUtility {
static const String unknown = "unknown";
static const String success = "success";
const ReadyDataUtility._();
}
@immutable
final class KeysHttpClientServiceUtility {
/* IPAddress */
static const String iPAddressQQIp = "ip";
const KeysHttpClientServiceUtility._();
}
@immutable
base class IPAddress extends BaseModel {
final String ip;
const IPAddress(this.ip) : super(ip);
@override
IPAddress clone() => IPAddress(ip);
@override
String toString() {
return "IPAddress(ip: $ip)";
}
}
@immutable
base class ListIPAddress<T extends IPAddress> extends BaseListModel<T> {
const ListIPAddress(super.listModel) : super();
@override
ListIPAddress<T> clone() {
List<T> newListModel = List.empty(growable: true);
for (final T model in listModel) {
newListModel.add(model.clone() as T);
}
return ListIPAddress<T>(newListModel);
}
@override
String toString() {
String strListModel = "\n";
for (final T itemModel in listModel) {
strListModel += "$itemModel,\n";
}
return "ListIPAddress(listModel: [$strListModel])";
}
}
@immutable
base class IPAddressWrapper extends BaseModelWrapper {
const IPAddressWrapper(super.listObject);
@override
IPAddress createModel() {
return IPAddress(listObject[0]);
}
}
@immutable
base class ListIPAddressWrapper extends BaseListModelWrapper {
const ListIPAddressWrapper(super.listsListObject);
@override
ListIPAddress createListModel() {
final List<IPAddress> listModel = List.empty(growable: true);
for (final List<dynamic> itemListObject in listsListObject) {
final iPAddressWrapper = IPAddressWrapper(itemListObject);
listModel.add(iPAddressWrapper.createModel());
}
return ListIPAddress(listModel);
}
}
abstract class BaseNamedHttpClient {
const BaseNamedHttpClient();
Future<http.Response> get(Uri url, {Map<String, String>? headers});
Future<http.Response> post(Uri url,
{Map<String, String>? headers, body, Encoding? encoding});
Future<void> close();
}
final class DefaultHttpClient extends BaseNamedHttpClient {
final http.Client _client;
const DefaultHttpClient(this._client);
@override
Future<http.Response> get(Uri url, {Map<String, String>? headers}) {
return _client.get(url, headers: headers);
}
@override
Future<http.Response> post(Uri url,
{Map<String, String>? headers, Object? body, Encoding? encoding}) {
return _client.post(url, headers: headers, body: body, encoding: encoding);
}
@override
Future<void> close() async {
_client.close();
}
}
final class TimeoutHttpClient extends BaseNamedHttpClient {
final http.Client _client;
final Duration _timeout;
const TimeoutHttpClient(this._client, this._timeout);
@override
Future<http.Response> get(Uri url, {Map<String, String>? headers}) {
return _client.get(url, headers: headers).timeout(_timeout);
}
@override
Future<http.Response> post(Uri url,
{Map<String, String>? headers, Object? body, Encoding? encoding}) {
return _client
.post(url, headers: headers, body: body, encoding: encoding)
.timeout(_timeout);
}
@override
Future<void> close() async {
_client.close();
}
}
abstract class BaseNamedHttpClientService {
const BaseNamedHttpClientService();
BaseNamedHttpClient? get getParameterNamedHttpClient;
}
final class DefaultHttpClientService extends BaseNamedHttpClientService {
static final DefaultHttpClientService instance = DefaultHttpClientService._();
BaseNamedHttpClient? _namedHttpClient;
DefaultHttpClientService._();
@override
BaseNamedHttpClient? get getParameterNamedHttpClient {
if (_namedHttpClient != null) {
return _namedHttpClient;
}
_namedHttpClient = DefaultHttpClient(http.Client());
return _namedHttpClient;
}
}
final class TimeoutHttpClientService extends BaseNamedHttpClientService {
static final TimeoutHttpClientService instance = TimeoutHttpClientService._();
BaseNamedHttpClient? _namedHttpClient;
TimeoutHttpClientService._();
@override
BaseNamedHttpClient? get getParameterNamedHttpClient {
if (_namedHttpClient != null) {
return _namedHttpClient;
}
_namedHttpClient =
TimeoutHttpClient(http.Client(), const Duration(seconds: 5));
return _namedHttpClient;
}
}
@immutable
base class IPAddressWrapperRepository<T extends IPAddressWrapper,
Y extends ListIPAddressWrapper> extends BaseModelWrapperRepository {
@protected
final BaseNamedHttpClientService namedHttpClientService;
const IPAddressWrapperRepository(this.namedHttpClientService);
@override
void dispose() {}
Future<ResultWithModelWrapper<T>> getIPAddressParameterNamedHttpClientService() async {
try {
final response = await namedHttpClientService.getParameterNamedHttpClient
?.get(Uri.parse(KeysUrlEndpointUtility.jsonipAPI));
if (response?.statusCode != 200) {
throw NetworkException.fromKeyAndStatusCode(this,
response?.statusCode.toString() ?? "", response?.statusCode ?? 0);
}
final Map<String, dynamic> data = jsonDecode(response?.body ?? "");
final ipByIPAddress = getSafeValueFromMapAndKeyAndDefaultValue(
data, KeysHttpClientServiceUtility.iPAddressQQIp, "");
return ResultWithModelWrapper.success(
IPAddressWrapper([ipByIPAddress]) as T);
} on NetworkException catch (e) {
return ResultWithModelWrapper.exception(e);
} catch (e) {
return ResultWithModelWrapper.exception(LocalException(
this, EnumGuilty.device, ReadyDataUtility.unknown, e.toString()));
}
}
}
enum EnumDataForMainVM { isLoading, exception, success }
final class DataForMainVM extends BaseDataForNamed<EnumDataForMainVM> {
IPAddress iPAddress;
DataForMainVM(super.isLoading, this.iPAddress);
@override
EnumDataForMainVM get getEnumDataForNamed {
if (isLoading) {
return EnumDataForMainVM.isLoading;
}
if (exceptionController.isWhereNotEqualsNullParameterException()) {
return EnumDataForMainVM.exception;
}
return EnumDataForMainVM.success;
}
@override
String toString() {
return "DataForMainVM(isLoading: $isLoading, "
"exceptionController: $exceptionController, "
"iPAddress: $iPAddress)";
}
}
final class MainVM {
// ModelWrapperRepository
final _iPAddressWrapperRepository = FactoryModelWrapperRepositoryUtility
.getIPAddressWrapperRepositoryFromNamedHttpClientService(
TimeoutHttpClientService.instance);
// TempCacheProvider
final _tempCacheProvider = TempCacheProvider();
// NamedUtility
// NamedStreamWState
late final BaseNamedStreamWState<DataForMainVM> _namedStreamWState;
MainVM() {
_namedStreamWState = DefaultStreamWState<DataForMainVM>(
DataForMainVM(true, const IPAddress("")));
}
Future<void> init() async {
_namedStreamWState.listenStreamDataForNamedFromCallback((event) {
_build();
});
final firstRequest = await _firstRequest();
debugPrint("MainVM: $firstRequest");
_namedStreamWState.notifyStreamDataForNamed();
}
void dispose() {
_iPAddressWrapperRepository.dispose();
_tempCacheProvider.dispose([]);
_namedStreamWState.dispose();
}
void _build() {
final dataWNamed = _namedStreamWState.getDataForNamed;
switch (dataWNamed.getEnumDataForNamed) {
case EnumDataForMainVM.isLoading:
debugPrint("Build: IsLoading");
break;
case EnumDataForMainVM.exception:
debugPrint(
"Build: Exception(${dataWNamed.exceptionController.getKeyParameterException})");
break;
case EnumDataForMainVM.success:
debugPrint("Build: Success(${dataWNamed.iPAddress})");
break;
}
}
Future<String> _firstRequest() async {
final getIPAddressParameterNamedHttpClientService =
await _iPAddressWrapperRepository
.getIPAddressParameterNamedHttpClientService();
if (getIPAddressParameterNamedHttpClientService.exceptionController
.isWhereNotEqualsNullParameterException()) {
return _firstQQFirstRequestQQGetIPAddressParameterNamedHttpClientService(
getIPAddressParameterNamedHttpClientService.exceptionController);
}
_namedStreamWState.getDataForNamed.isLoading = false;
_namedStreamWState.getDataForNamed.iPAddress =
getIPAddressParameterNamedHttpClientService.modelWrapper!.createModel();
return ReadyDataUtility.success;
}
Future<String>
_firstQQFirstRequestQQGetIPAddressParameterNamedHttpClientService(
ExceptionController exceptionController) async {
_namedStreamWState.getDataForNamed.isLoading = false;
_namedStreamWState.getDataForNamed.exceptionController =
exceptionController;
return exceptionController.getKeyParameterException;
}
}
Future<void> main() async {
final mainVM = MainVM();
await mainVM.init();
mainVM.dispose();
}
输出结果
期望输出:
MainVM: success
Build: Success(IPAddress(ip: ${your_ip}))
Process finished with exit code 0
或者
===start_to_trace_exception===
WhereHappenedException(Class) --> ${WhereHappenedException(Class)}
NameException(Class) --> ${NameException(Class)}
toString() --> ${toString()}
===end_to_trace_exception===
MainVM: ${getKeyParameterException}
Build: Exception(${getKeyParameterException})
Process finished with exit code 0
更多关于Flutter MVVM架构插件library_architecture_mvvm_modify的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter MVVM架构插件library_architecture_mvvm_modify的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用library_architecture_mvvm_modify
插件来构建MVVM架构的一个简单示例。请注意,由于library_architecture_mvvm_modify
这个库名称看起来是一个假设的或者特定定制的库(Flutter社区中并没有一个广泛认知的同名库),我将基于一般MVVM架构的原理和Flutter的常规实践来展示一个示例代码结构。
1. 项目结构
首先,我们定义一个基本的项目结构来支持MVVM架构:
my_flutter_app/
├── lib/
│ ├── data/ # 数据层
│ │ ├── repository/
│ │ │ └── user_repository.dart
│ ├── model/ # 数据模型层
│ │ └── user.dart
│ ├── view/ # 界面层
│ │ └── user_screen.dart
│ ├── viewmodel/ # ViewModel层
│ │ └── user_viewmodel.dart
│ └── main.dart
2. 数据模型 (Model)
在model
文件夹中定义数据模型,例如一个User
类:
// lib/model/user.dart
class User {
final String name;
final int age;
User({required this.name, required this.age});
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
};
}
}
3. 数据访问层 (Data Layer)
在data/repository
文件夹中定义一个仓库类,用于处理数据访问逻辑:
// lib/data/repository/user_repository.dart
import 'package:flutter/material.dart';
import 'package:my_flutter_app/model/user.dart';
class UserRepository {
// 模拟从API获取用户数据
Future<User> fetchUser() async {
await Future.delayed(Duration(seconds: 2)); // 模拟网络延迟
return User(name: 'John Doe', age: 30);
}
}
4. ViewModel
在viewmodel
文件夹中定义一个ViewModel类,它负责处理业务逻辑并管理UI状态:
// lib/viewmodel/user_viewmodel.dart
import 'package:flutter/material.dart';
import 'package:my_flutter_app/data/repository/user_repository.dart';
import 'package:my_flutter_app/model/user.dart';
class UserViewModel with ChangeNotifier {
User? _user;
User? get user => _user;
bool isLoading = false;
bool hasError = false;
String? errorMessage;
final UserRepository _userRepository = UserRepository();
void fetchUser() async {
isLoading = true;
hasError = false;
errorMessage = null;
try {
_user = await _userRepository.fetchUser();
} catch (_) {
hasError = true;
errorMessage = 'Failed to fetch user data.';
} finally {
isLoading = false;
notifyListeners();
}
}
}
5. 界面层 (View)
在view
文件夹中定义一个Widget,它使用ViewModel来展示数据并处理用户交互:
// lib/view/user_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:my_flutter_app/viewmodel/user_viewmodel.dart';
class UserScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final userViewModel = Provider.of<UserViewModel>(context);
return Scaffold(
appBar: AppBar(title: Text('User Info')),
body: Center(
child: userViewModel.isLoading
? CircularProgressIndicator()
: userViewModel.hasError
? Text(userViewModel.errorMessage!)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Name: ${userViewModel.user!.name}'),
Text('Age: ${userViewModel.user!.age}'),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: userViewModel.fetchUser,
tooltip: 'Fetch User',
child: Icon(Icons.refresh),
),
);
}
}
6. 主程序入口 (Main Entry)
在main.dart
中设置Provider,并指定根Widget:
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:my_flutter_app/viewmodel/user_viewmodel.dart';
import 'package:my_flutter_app/view/user_screen.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserViewModel()),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter MVVM Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: UserScreen(),
);
}
}
总结
以上代码展示了如何在Flutter中使用MVVM架构模式,通过分层的方式将应用逻辑分离到不同的组件中。虽然library_architecture_mvvm_modify
这个库可能不存在,但基于上述示例,你可以很容易地扩展或修改以满足特定需求。希望这能帮助你理解和实现Flutter中的MVVM架构。