Flutter设计模式

文章目录

  • Flutter设计模式
    • 代码
    • 参考文档
    • 单例模式
      • 项目中的使用
    • 工厂模式
      • 简单工厂模式
      • 项目中的使用(简单工厂)
      • 工厂方法模式
      • 项目中使用(工厂方法模式)
      • 抽象工厂模式
    • 观察者模式
      • 普通观察者模式
    • 适配器模式
      • 对象适配器
      • 类适配器

代码

https://gitee.com/hellosunshine/design-mode-study-project.git
https://gitee.com/hellosunshine/bilibili_flutter_getx.git

参考文档

https://flutter.cn/community/tutorials/singleton-pattern-in-flutter-n-dart
https://juejin.cn/post/7072625679296102413
https://flutter.cn/docs/cookbook/persistence/key-value
实线与虚线
继承与实现
https://blog.csdn.net/weixin_29230649/article/details/114773522

单例模式

  • 单例类中包含一个引用自身类的静态属性实例,且能自行创建这个实例
  • 改实例只能通过静态方法访问
  • 类构造函数通常没有参数,且被标记为私有,确保不能从类外部实例化该类
///单例
class Singleton {///私有的命名构造函数Singleton._internal();///声明一个单例static final Singleton instance = Singleton._internal();///工厂构造函数返回单例factory Singleton() => instance;
}
///非单例
class NotSingleton {}main() {///创建多个实例,但单例只会有一个Singleton singletonOne = Singleton();Singleton singletonTwo = Singleton();print(singletonOne == singletonTwo); //tureNotSingleton notSingletonOne = NotSingleton();NotSingleton notSingletonTwo = NotSingleton();print(notSingletonOne == notSingletonTwo); //false
}
项目中的使用
  • shared_preferences插件(轻量化存储数据)源码里单例
import 'dart:convert';import 'package:shared_preferences/shared_preferences.dart';///持久化存储数据
class SharedPreferenceUtil {static late SharedPreferences _preferences;///初始化SharedPreferencestatic void initSharedPreference() async {_preferences = await SharedPreferences.getInstance();}
}
  • 对Dio网络请求单例
import 'package:dio/dio.dart';class HttpBaseRequest {///单例late Dio _dio;HttpBaseRequest._internal() {_dio = Dio();}static final HttpBaseRequest _instance = HttpBaseRequest._internal();factory HttpBaseRequest() => _instance;Future request(String path) async {final result = await _dio.get(path);return result;}
}

工厂模式

简单工厂模式

抽象生产对象

  • 定义抽象类
///定义一个抽象方法 人,人会跑
abstract class Person {void run();
}
  • 生产对象
///男人
class Man extends Person {@overridevoid run() {print("man can run");}
}///女人
class Woman extends Person {@overridevoid run() {print("woman can run");}
}
  • 调用
class SimpleFactoryMode {static void createProduct(int type) {if (type == 1) {Man().run();}if (type == 2) {Woman().run();}}
}
main() {SimpleFactoryMode.createProduct(1); //man can runSimpleFactoryMode.createProduct(2); //woman can run
}
项目中的使用(简单工厂)

场景:在【Flutter】(聊天)中,用户可以发送各类数据,包括文本类型数据、图片数据、视频数据、音频数据。
构建类图

@startuml SendDataDialog
class BaseSendDataModel{List users; //接受者列表String sender; //发送者int date; //日期时间戳String avatar; //头像
}
abstract SendDataModel {+String() buildString //生成数据文本格式
}
note left: 抽象用户发送的数据class TextData {String msg; //文字+String() buildString
}class PictureData {File file; //图片文件+String() buildString
}class VideoData {File file; //视频文件+String() buildString
}class audioData {File file; //音频文件+String() buildString
}class SendDataFactory {{abstract} SendDataModel() createSendData //创建发送消息
}
note left: 简单工厂
BaseSendDataModel <--* SendDataModel
SendDataModel <|.. TextData
SendDataModel <|.. PictureData
SendDataModel <|.. VideoData
SendDataModel <|.. audioData
TextData <.. SendDataFactory
PictureData <.. SendDataFactory
VideoData <.. SendDataFactory
audioData <.. SendDataFactory
@enduml
  • 创建抽象类(dart不支持interface)
///生产发送数据的简单工厂
class BaseSendDataModel {late List<String> users; //接受者列表late String sender; //发送者late int date; //日期时间戳late String avatar;BaseSendDataModel({required this.users,required this.sender,required this.date,required this.avatar,}); //头像
}abstract class SendDataModel {late BaseSendDataModel baseSendDataModel;String buildString(); //生成数据文本格式
}
  • 实现接口,并构建文字数据类和图片数据类
///文本类型数据
class TextData implements SendDataModel {late String msg;late String dataStr;TextData({required this.msg,required this.baseSendDataModel,});String buildString() {String dataStr = "{\"users\": [\"${baseSendDataModel.users.toString()}\"],""\"msg\": \"$msg\",""\"date\": \"${baseSendDataModel.date}\",""\"avatar\": \"${baseSendDataModel.avatar}\",""\"sender\": \"${baseSendDataModel.sender}\"}";return dataStr;}BaseSendDataModel baseSendDataModel;
}///图片数据
class PhotoData implements SendDataModel {late String photoFilePath;PhotoData({required this.photoFilePath,required this.baseSendDataModel,});String buildString() {String dataStr = "{\"users\": [\"${baseSendDataModel.users.toString()}\"],""\"msg\": \"$photoFilePath\","// "\"msg\": \"${MultipartFile.fromFileSync(photoFilePath)}\",""\"date\": \"${baseSendDataModel.date}\",""\"avatar\": \"${baseSendDataModel.avatar}\",""\"sender\": \"${baseSendDataModel.sender}\"}";return dataStr;}BaseSendDataModel baseSendDataModel;
}
  • 构建简单工厂类
///发送数据类型
enum SendDataType {text,video,audio,photo,
}///简单工厂类
class SendDataFactory {static SendDataModel createSendData({required SendDataType sendDataType,required BaseSendDataModel baseSendDataModel,String? msg,String? photoFilePath,}) {if (sendDataType == SendDataType.text) {return TextData(msg: msg!,baseSendDataModel: baseSendDataModel,);} else if (sendDataType == SendDataType.photo) {return PhotoData(photoFilePath: photoFilePath!,baseSendDataModel: baseSendDataModel,);} else {throw Exception();}}
}
  • 实现
main() {BaseSendDataModel baseSendDataModel = BaseSendDataModel(users: ["2", "3"],sender: '4',date: 5,avatar: '6',);SendDataModel sendTextData = SendDataFactory.createSendData(sendDataType: SendDataType.text,baseSendDataModel: baseSendDataModel,msg: "我发送了文本消息");SendDataModel sendPhotoData = SendDataFactory.createSendData(sendDataType: SendDataType.photo,baseSendDataModel: baseSendDataModel,photoFilePath: "这是一张图片地址");String textJson = sendTextData.buildString();String photoJson = sendPhotoData.buildString();print(textJson);print(photoJson);
}

-运行

{"users": ["[2, 3]"],"msg": "我发送了文本消息","date": "5","avatar": "6","sender": "4"}
{"users": ["[2, 3]"],"msg": "这是一张图片地址","date": "5","avatar": "6","sender": "4"}Process finished with exit code 0
工厂方法模式

抽象类方法

  • 定义一个抽象类,里面声明抽象方法及业务方法
abstrast class LearningFactory {///抽象方法String learning();///业务代码void show() {String skill = learning();print(skill)}
}
  • 子类实现这个抽象类,并重写抽象方法
class LearningWalk extends LearningFactory {@overrideString learning() {return "I can walk";}
}class LearningRun extends LearningFactory {@overrideString learning() {return "I can run";}
}
  • 实例化子类,调用业务方法
main() {final walkResult = LearningWalk();final runResult = LearningRun();walkResult.show();runResult.show();
}
项目中使用(工厂方法模式)

场景:应对Android和IOS分别实现拍摄媒体功能

@startuml
abstract TakeMediaFactory {+String() methodName+void() invoke
}
note left: 拍摄媒体
class AndroidTakeMedia {+String() methodName
}
class IosTakeMedia {+String() methodName
}
TakeMediaFactory <-- AndroidTakeMedia
TakeMediaFactory <-- IosTakeMedia
@enduml
  • 创建抽象类
///拍摄媒体(Android & Ios)
abstract class TakeMediaFactory {late MethodChannel takeMediaChannel;///构建拍摄媒体界面String methodName();///展示void invoke() {ChannelUtil().takeMediaChannel.invokeMethod(methodName(), "").then((value) => print(value));}
}
  • 定义子类
///Android拍摄媒体
class AndroidTakeMedia extends TakeMediaFactory {String methodName() {return "takeMediaAndroid";}
}///ios拍摄媒体
class IosTakeMedia extends TakeMediaFactory {String methodName() {return  "takeMediaIos";}
}
  • Android端监听通道(MainActivity.java)
package com.example.take_media_factory_project;import android.content.Intent;
import android.os.Bundle;import androidx.annotation.Nullable;import java.util.Objects;import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;public class MainActivity extends FlutterActivity {private static final String takeMediaChannel = "take_media_channel";@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);new MethodChannel(Objects.requireNonNull(getFlutterEngine()).getDartExecutor().getBinaryMessenger(), takeMediaChannel).setMethodCallHandler((call, result) -> {if ("takeMediaAndroid".equals(call.method)) {openTakeMediaView();result.success("启动Android的拍摄功能");} else {result.success("没有对应的方法");}});}private void openTakeMediaView() {Intent intent = new Intent(this, TakeMediaActivity.class);startActivity(intent);}
}
  • TakeMediaActivity.java
package com.example.take_media_factory_project;import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.Preview;
import androidx.camera.core.UseCaseGroup;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;import com.google.common.util.concurrent.ListenableFuture;import java.util.concurrent.ExecutionException;public class TakeMediaActivity extends AppCompatActivity {//拍照Button takePhotoButton;//预览PreviewView previewView;//权限private static final String[] REQUIRE_PERMISSION = new String[]{Manifest.permission.CAMERA};public static final int REQUEST_CODE_PERMISSIONS = 10;//captureImageCapture imageCapture;ListenableFuture<ProcessCameraProvider> processCameraProviderListenableFuture;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_take_media);takePhotoButton = findViewById(R.id.takePhotoBtn);previewView = findViewById(R.id.preview_view);if (havePermissions()) {initCamera();} else {ActivityCompat.requestPermissions(this, REQUIRE_PERMISSION, REQUEST_CODE_PERMISSIONS);}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == REQUEST_CODE_PERMISSIONS) {initCamera();} else {finish();}}private boolean havePermissions() {for (String permission : REQUIRE_PERMISSION) {if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {return false;}}return true;}private void initCamera() {imageCapture = new ImageCapture.Builder().setFlashMode(ImageCapture.FLASH_MODE_ON).build();processCameraProviderListenableFuture = ProcessCameraProvider.getInstance(this);processCameraProviderListenableFuture.addListener(() -> {try {previewView.setScaleType(PreviewView.ScaleType.FIT_CENTER);Preview preview = new Preview.Builder().build();preview.setSurfaceProvider(previewView.getSurfaceProvider());ProcessCameraProvider processCameraProvider = processCameraProviderListenableFuture.get();processCameraProvider.unbindAll();UseCaseGroup useCaseGroup = new UseCaseGroup.Builder().addUseCase(preview).addUseCase(imageCapture).build();processCameraProvider.bindToLifecycle((LifecycleOwner) TakeMediaActivity.this, CameraSelector.DEFAULT_BACK_CAMERA, useCaseGroup);} catch (ExecutionException | InterruptedException e) {e.printStackTrace();}}, ContextCompat.getMainExecutor(this));}
}
  • activity_take_media.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"tools:context=".TakeMediaActivity"><androidx.camera.view.PreviewViewandroid:id="@+id/preview_view"android:layout_width="match_parent"android:layout_height="match_parent" /><Buttonandroid:id="@+id/takePhotoBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/takePhoto"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias=".9" />
</androidx.constraintlayout.widget.ConstraintLayout>
  • IOS端待补充
  • 调用
    • 通道单例
import 'package:flutter/services.dart';class MediaChannel {static String takeMediaChannel = "take_media_channel";
}class ChannelUtil {late MethodChannel takeMediaChannel;ChannelUtil._internal() {///获取媒体takeMediaChannel = MethodChannel(MediaChannel.takeMediaChannel);}static final ChannelUtil _instance = ChannelUtil._internal();factory ChannelUtil() => _instance;
}
  • main.dart
void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatefulWidget {State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(onPressed: () {if (defaultTargetPlatform == TargetPlatform.android) {AndroidTakeMedia().invoke();} else if (defaultTargetPlatform == TargetPlatform.iOS) {IosTakeMedia().invoke();}},child: Icon(Icons.phone_iphone_outlined),),);}
}
抽象工厂模式

抽象生产对象的工厂

  • 定义抽象的工厂类
abstract class ElectronicProductFactory {Product createComputer();Product createMobile();Product createPad();
}
class Product {String? name;factory Product.createProductOne(String name) {return productOne;}Product._createProductOne();static Product productOne = Product._createProductOne();
}
  • 生成一系列对象,他们之间存在一定的联系
///苹果
class Apple extends ElectronicProductFactory {@overrideProduct createComputer() {return Product.createProductOne("Mac");}@overrideProduct createMobile() {return Product.createProductOne("IPhone");}@overrideProduct createPad() {return Product.createProductOne("IPad");}
}///小米
class XiaoMi extends ElectronicProductFactory {@overrideProduct createComputer() {return Product.createProductOne("RedMi");}@overrideProduct createMobile() {return Product.createProductOne("MIUI");}@overrideProduct createPad() {return Product.createProductOne("MIPad");}}

观察者模式

普通观察者模式
  • 定义通知和观察者类
///观察者类
class Observer {String name;Observer(this.name);void notify(Notification notification) {print("[${notification.timestamp.toIso8601String()}] Hey$name,${notification.message}!");}
}///通知
class Notification {late DateTime timestamp;late String message;Notification.forNow(String name) {timestamp = DateTime.now();message = "msg:$name";}
}
  • 创建一个被观察者类
///被观察者
class Subject {late List<Observer> _observers;///传入观察者列表Subject([List<Observer>? observers]) {_observers = observers ?? [];}///注册观察者void registerObserver(Observer observer) {_observers.add(observer);}///注销观察者void unregisterObserver(Observer observer) {_observers.remove(observer);}///通知观察者void notifyObservers(Notification notification) {for (var observer in _observers) {observer.notify(notification);}}
}
  • 创建业务类
///创建一个业务类CoffeeMaker继承被观察者类Subject
class CoffeeMaker extends Subject {CoffeeMaker([List<Observer>? observers]) : super(observers);void brew() {print("Brewing the coffee...");notifyObservers(Notification.forNow("coffee's done"));}
}
  • 测试
void main() {///创建观察者var me = Observer("Tyler");///初始化观察者列表var mrCoffee = CoffeeMaker(List.from([me]));///创建观察者var myWife = Observer("Kate");///注册观察者mrCoffee.registerObserver(myWife);///通知观察者mrCoffee.brew();
}

适配器模式

适配器模式可以将不兼容的接口转为可以兼容的接口

对象适配器
  • 定义被适配类
import 'dart:convert';
import 'package:xml/xml.dart';
///被适配类
class TargetAdapter {String name;TargetAdapter({required this.name});
}

-规范化结构定义

///规范化结构定义
abstract class ITarget {TargetAdapter getTargetAdapterList();
}
  • 不同的对象适配器
///对象适配器
///种类A(json类型的string)
class DataBox {static String receivedJsonData = '''{"name": "data from json"}''';static String receivedXmlData = '''<name>date from xml</name>''';
}class TypeJsonAdapter extends ITarget {var targetAdapter = TargetAdapter();TargetAdapter getTargetAdapterList() {final jsonData = json.decode(DataBox.receivedJsonData);String name = jsonData["name"];targetAdapter.name = name;return targetAdapter;}
}///种类B(xml类型的string)
class TypeXmlAdapter extends ITarget {var targetAdapter = TargetAdapter();TargetAdapter getTargetAdapterList() {XmlDocument xmlDocument = XmlDocument.parse(DataBox.receivedXmlData);String text = xmlDocument.getElement("name")!.text;targetAdapter.name = text;return targetAdapter;}
}
  • 客户端调用
///客户端调用
class Client {late ITarget iTarget;Client({required this.iTarget});getString() {final result = iTarget.getTargetAdapterList();print(result.name);}
}main() {Client clientJson = Client(iTarget: TypeJsonAdapter());Client clientXml = Client(iTarget: TypeXmlAdapter());clientJson.getString(); //data from jsonclientXml.getString(); //date from xml
}
类适配器
  • 案例
class TargetAdapter {String conCreate() {return "targetAdapter";}}class ClassAdapter extends TargetAdapter {String operate() {return super.conCreate();}
}
  • SliverToBoxAdapter案例
    CustomScrollView的Sliver属性接收Sliver系列组件
  • SliverToBoxAdapter源码
    SliverToBoxAdapter
class SliverToBoxAdapter extends SingleChildRenderObjectWidget {/// Creates a sliver that contains a single box widget.const SliverToBoxAdapter({super.key,super.child,});RenderSliverToBoxAdapter createRenderObject(BuildContext context) => RenderSliverToBoxAdapter();
}

这里child传给的父类SingleChildRenderObjectWidget,这里重写抽象类RenderObjectWidget中的createRenderObject
最终返回了RenderSliverToBoxAdapter()

一句话就是将 SingleChildRenderObjectWidget 中 createRenderObject 接口重写转换成可以包含 RenderBox (对应一般 widget 的 RenderObject) 的 RenderSliver (对应 sliver 系列 widget 的 RenderObject),即这里的 RenderSliverToBoxAdapter

【Flutter】设计模式(更新)相关推荐

  1. Flutter 页面更新流程剖析

    文章目录 Flutter页面更新流程剖析 更新流程 渲染过程 视频课程 博主相关文章列表 Flutter 框架实现原理 Flutter 框架层启动源码剖析 Flutter 页面更新流程剖析 Flutt ...

  2. flutter 热更新

    flutter热更新主要是更新代码逻辑和资源两方面.flutter的编译产物刚好吧两个部分分开了.代码逻辑是libapp.so, 资源在flutter_assets目录.要更新flutter就要对li ...

  3. Flutter 热更新功能实现

    Flutter 官方在 GitHub 上声明是暂时不支持热更新的,但是还是有很多能人,通过一些自己的手段,在Android端是能够实现动态更新的功能的. 先看下flutter 的apk 和普通的apk ...

  4. 一起看 I/O | Flutter 3 更新详解

    作者 / Kevin Jamaul Chisholm, Technical Program Manager for Dart and Flutter at Google 又到了 Flutter 稳定版 ...

  5. Flutter热更新与热加载

    之前偶然从Flutter官方文档上看到了支持热更新,当然这是从2019年才开始的 Dynamic updates The Dart Platform, on which Flutter is buil ...

  6. Android-史上最优雅的实现文件上传、下载及进度的监听,flutter热更新方案

    }); 注:如果需要对Http的返回值做解析,可在使用from操作符时,传入一个解析器Parser 带进度上传 带进度上传使用uploadProgress操作符,并结合doOnNext.filter. ...

  7. Flutter App更新升级

    1. 应用程序升级流程 由于在 IOS 中没法直接下载安装,如果版本不一致则直接跳转到IOS应用对应的应用市场就可以了,所以本文仅介绍Android App的升级流程. Android App升级流程 ...

  8. flutter不支持热更新_Flutter 在安卓上可以实现热更新了

    本文由 句号君 授权投稿 原文链接:https://blog.csdn.net/qizewei123/article/details/102963340 Flutter 官方在 GitHub 上声明是 ...

  9. Flutter 2.2 更新详解

    Flutter 2.2 版[1]已正式发布!要获取新版本,您只需切换到 stable 渠道并更新目前安装的 Flutter,或前往 flutter.cn/docs/get-started[2] 从头开 ...

最新文章

  1. C++ 多线程:时间控制
  2. cs6 数据库mysql_能mysql内容
  3. CSS3实例教程:border-image属性实例讲解
  4. 【CMD】复制并覆盖目标文件
  5. Redis Sentinel 配置文件
  6. asp.net core 从 3.1 到 5.0
  7. java 下拉列表 枚举_「Java三分钟」精准而优雅——枚举类详解
  8. 今年新增院士中,最年轻的是他
  9. 前端向后台发送请求有几种方式?
  10. java实现数据库回滚,java 数据库操作,事宜回滚
  11. 查询EI检索号的方法
  12. 客户端单周发版下的多分支自动化管理与实践
  13. BlazeFace测试
  14. 安装allennlp
  15. laravel组件单独加载(2):模型 Eloquent ORM
  16. 南阳理工学院计算机专业很强吗,南阳理工学院最好的专业?实力最强的是那个专业...
  17. 原子操作-atomic
  18. 数据库系统——第九讲 嵌入式SQL语言之基本技巧
  19. 一个GPIB操作的C#类
  20. 暑假学习计划回顾总结二

热门文章

  1. android是手机自拍,安卓6大摄影手机推荐,给喜欢自拍的你
  2. 【敲级实用】:某小伙写了一个的办公脚本后~变精神了~
  3. cJSON支持64位数据解析
  4. STM32中断系统的基本概念
  5. jsp内置对象--request对象
  6. 计算机考证word特殊符号
  7. 45个有助于英文学术论文写作的神网站
  8. 收藏 不显示删除回复显示所有回复显示星级回复显示得分回复 经典的60句话,慢慢体会 ^_^[...
  9. 神经网络参数优化算法,神经网络参数优化方案
  10. php crypt,PHP crypt() 函数