{"id":19741774,"url":"https://github.com/ziadasem/design-patterns-in-dart","last_synced_at":"2025-07-06T04:39:49.865Z","repository":{"id":242731047,"uuid":"810079400","full_name":"ziadasem/Design-patterns-in-dart","owner":"ziadasem","description":"selected design patterns implemented with dart programming language, oriented for flutter development","archived":false,"fork":false,"pushed_at":"2024-06-08T22:46:20.000Z","size":31,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-10T20:17:03.382Z","etag":null,"topics":["adapter-pattern","dart","design-patterns","facade-pattern","factory-pattern","flutter","observer-pattern","singleton","strategy-pattern"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ziadasem.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-04T02:22:24.000Z","updated_at":"2024-10-10T01:53:35.000Z","dependencies_parsed_at":"2024-11-12T01:30:49.956Z","dependency_job_id":"65799bb1-5f34-4bde-b6ec-dd6a7b2ce921","html_url":"https://github.com/ziadasem/Design-patterns-in-dart","commit_stats":null,"previous_names":["ziadasem/design-patterns-in-dart"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ziadasem%2FDesign-patterns-in-dart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ziadasem%2FDesign-patterns-in-dart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ziadasem%2FDesign-patterns-in-dart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ziadasem%2FDesign-patterns-in-dart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ziadasem","download_url":"https://codeload.github.com/ziadasem/Design-patterns-in-dart/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241072414,"owners_count":19904759,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adapter-pattern","dart","design-patterns","facade-pattern","factory-pattern","flutter","observer-pattern","singleton","strategy-pattern"],"created_at":"2024-11-12T01:27:47.754Z","updated_at":"2025-02-27T23:19:21.128Z","avatar_url":"https://github.com/ziadasem.png","language":null,"readme":"# Exploring Design Patterns in Dart \nDesign patterns in Dart are explored in this article, providing insights into their importance, implementation, and utility. A selection of patterns from the three main categories (creational, structural, and behavioral), is focused on, with examples in Dart demonstrating their usage. References from authoritative sources such as Refactoring Guru and tutorials from TutorialsPoint are used.\n\n## 1. Introduction\nIn software engineering, a design pattern is a general repeatable solution to a commonly occurring problem in software design. A design pattern isn't a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations.\n\n### Patterns vs algorithms:\n\nPatterns are often confused with algorithms because both concepts describe typical solutions to some known problems. \n\nWhile an algorithm always defines a **clear set of actions that can achieve some goal**, a pattern is **a more high-level description of a solution**. The code of the same pattern applied to two different programs may be different.\n\nfor example, take the `binary search algorithm` and `Factory pattern` as an example. This algorithm implies certain clear steps to implement. for example, the List should sorted, three checks should be made, and the update of index.\n\nwhile for Factory pattern it is stated as to create an object without exposing the creation logic to the client and refer to newly created object using a common interface. without providing concise steps or methods, as it is free to the developer.\n\nSo, to learn a design pattern, a different mindset is required than the mindset of using an algorithm.\n\n### Elements of a Design Pattern \n![mindmap](https://i.ibb.co/wh8kF6G/ele.jpg)\n\n### Classification of design patterns\n```\n\t\t\t\t\t\t\t\t\t\t\tClassification of design patterns\n\t\t\t\t\t\t\t\t\t\t\t\t    |\n\t\t\t\t\t\t\t\t\t\t\t\t    |\n\t\t___________________________________________________________________________________________________________________________________________\n\t\t↓\t\t\t\t\t\t\t\t\t\t    ↓\t\t\t\t\t\t\t  ↓\n\t\t↓\t\t\t\t\t\t\t\t\t            ↓\t\t\t\t\t\t\t  ↓\n\tCreational patterns\t\t\t\t\t\t\t\t     Structural patterns\t\t\t\tBehavioral patterns\nprovide object creation mechanisms that \t\t\t\t\tPatterns that describes how to assemble \t\ttake care of effective communication and the \nincrease flexibility and reuse of existing\t\t\t\t\tobjects and classes together as a larger   \t\tassignment of responsibilities between objects\ncode.\t\t\t\t\t\t\t\t\t\t\tflexible structure\n(Factory Method, Abstract Factory , Singleton)  \t\t\t\t(Adapter Method, Bridge method , Facade)\t\t(Command Method, Strategy, Observer)\n\n```\n## 2. Creational Design Patterns\n\n### 2.1 Factory Method Design pattern\n* separates the object creation logic from the client code\n* a factory can  return different objects belonging to the same super-class, with the same method\n\nwhen you have buttons class; one for Windows, one for MacOS, one for Web, one for Android, and one for IOS. you can create as many classes as you want to instantiate, **OR, you can use a factory design pattern**.\n\n**Problem**:\nsuppose we write a cross-platform program that renders a button according to the running OS, and we have this piece of code:\n```dart\nreturn Scaffold (\n\tbody:\n\t\tisWeb ? WebButton()\n\t\t: isAndroid ? AndroidButton()\n\t\t: isMacOS ? MacButton() \n\t\t: isWindows ? WindowsButton()\n\t\t: IosButton()\n)\n```\nas you can see many ternary if-conditions are written in to decide which object to pass.\n\n**The Solution**:\nall of these objects are `Button`, so they can implement Button interface, and every subclass will override its methods \n```dart\nclass Button { //no interface in dart\n\tvoid onClick(){}\n\tvoid render(){}\n}\n```\nand for the derived classes:\n```dart\nclass WButton implements Button{\n\t@Override\n\tvoid draw(){ print(\"draw windows button\");}\n\n\t@Override\n\tvoid onClick(){ print(\"draw windows button\");}\n}\n\nclass AButton implements Button{\n\t@Override\n\tvoid draw(){ print(\"draw Android button\");}\n\n\t@Override\n\tvoid onClick(){ print(\"draw Android button\");}\n}\n\nclass WebButton implements Button{\n\t@Override\n\tvoid draw(){ print(\"draw web button\");}\n\n\t@Override\n\tvoid onClick(){ print(\"draw web button\");}\n}\n```\n\n**Create button factory class**\nAfter creating objects that implement the same interface, now we will create a Factory class, which is a class with the creation logic that returns the appropriate Button once.\n```dart\nclass ButtonFactory {\n\tButton getButton(){ \n\t\tif (isWeb){\n\t\t\treturn WebButton();\n\t\t}else if (isAndroid){\n\t\t\treturn AndroidButton();\n\t\t}....\n}\n```\n\nnow update the first code snippet to fix the issue:\n```dart\nreturn Scaffold (\n\tbody:\n\t\tButtonFactory.getButton(),\n)\n```\nnow we can add more buttons without affecting the client code.\n\n### 2.2 Singleton Design pattern\ncreats one instance of object in the runtime, it is useful for classes that connects to database or classes that loads configuration\n```dart\nclass Calender {\n\t Calender._() {} //disable calling to the constructor by makeing the constructor private\n\t static Calender _instance;\n\n\t static getInstance() {\n\t\tif (_instance == null) {\n\t\t\t_instance =  Calender();\n\t\t}\n\t\treturn _instance ;\n\t }\n}\n```\n\n\n## 3. Structural Design Patterns\n\n### 3.1 Adapter Design Patterns\n\nThe adapter design pattern is a structural design that allows two objects with different interfaces to collaborate.\n\n  ![block digaram for classes](https://i.ibb.co/5xz02K0/Capture.png)\n\nWe want the Media Player class to collaborate with Video Editor to take advantage of Video Player, but both of them are incompatible classes, hence we will build an ADAPTER.  \n  \n```dart\nclass  ImageViewer {\n\tvoid  showImage(){ }\n}\n\nclass  VideoPlayer {\n\tvoid  showVideo(){}\n}\n\n\nclass  MediaPlayer  implements  ImageViewer {\n\n\t@override\n\tvoid  showImage(){\n\t\tprint(\"Image\");\n\t}\n}\n\nclass  VideoEditor  implements  VideoPlayer {\n\t@override\n\tvoid  showVideo(){\n\t\tprint(\"video\");\n\t}\n}\n\nvoid  main(){\n\n\tMediaPlayer  mediaPlayer  =  MediaPlayer();\n\tmediaPlayer.showVideo(); //we want to add this to media player,\n\t//and media player isn't a type of video player\n\t//hence we will use an adapter/wrapper\n}\n```\n  \n\nThe adapter:\n\n ```dart  \n\nclass  MediaPlayerAdapter  implements  VideoPlayer{\n\n\tImageViewer  imageViewer ;\n\tMediaPlayerAdapter(this.imageViewer);\n\n\t@override\n\tvoid  showVideo(){\n\t\t//show Video logic\n\t}\n}\n\n \nvoid  main(){\n\tMediaPlayer  mediaPlayer  =  MediaPlayer();\n\t//VideoPlayer videoPlayer = VideoPlayer() ;\n\tMediaPlayerAdapter  adapter  =  MediaPlayerAdapter(mediaPlayer);\n\tadapter.showVideo();\n\tadapter.imageViewer.showImage();\n}\n```\n\nNow they can be interfaced\n\n  \n\n### 3.2 Decorative Design Patterns\n\n\nA decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.\n\nAs a structural pattern, this pattern allows to attachment of new functionality to the class without altering the base one or creating a new one.\n\n\n**Example**:\n\nWe have a file reader, text reader, and image reader classes all of them are sub-classes (variants) from the base class Reader. For every reader, it has two variants which are the Encrypted reader and the translated reader. I.e. (i.e. encrypted file reader, translated text reader, …).\n\nHow many classes do we want to create?\n\nWe need to create 3(file, text, and image) * 2(encrypted and translated) = 6 classes. Now suppose we have a new variant and two new readers how many new classes we should make?\n\n(3 + 2 ) * (2+1) = 18 total, 18-6 = 12!\n\n\nFor just adding three new types we will write a new 12 classes. This will ruin the structure of the code. The solution is to use the decorator design pattern.\n\nThe fact that all readers belong to the same interface and the result sub-class is just a reader with added functionality. we can make a class that takes a reader as input and attaches to it a functionality then outputs it.\n\n![block diagram](https://i.ibb.co/vDNjd5D/Capture.png)\n\nHere an object of File Reader is passed to an object of Encryption Class and outputs Encrypted Files Reader. Note the following Figure six objects are created from five objects only if we want a new variant we only create a new Decorator object instead of writing three objects\n\n![block diagram](https://i.ibb.co/5xsp4V8/Capture.png)\n\n```dart\nclass  Reader {\n\tString  read(){return  \"N/A\";}\n\tvoid  write(String  data){}\n}\n\n\nclass  FileReader  implements  Reader {\n\tString  value  =  \"\" ;  \n\t\n\t@override\n\tString  read() {\n\t\treturn  \"$value File\" ;\n\t}\n\n\n\t@override\n\tvoid  write(String  value) {\n\t\tthis.value  =  value ;\n\t}\n}\n\n \nclass  TextReader  implements  Reader {\n\n\tString  value  =  \"\" ;\n\t\n\t@override\n\tString  read() {\n\t\treturn  \"$value Text\" ;\n\t}\n\n\t@override\n\tvoid  write(String  value) {\n\t\tthis.value  =  value ;\n\t}\n\n}\n\nclass  ImageReader  implements  Reader {\n\n\tString  value  =  \"\" ;\n\t\n\t@override\n\tString  read() {\n\t\treturn  \"$value image\" ;\n\t}\n\n\t@override\n\tvoid  write(String  value) {\n\t\tthis.value  =  value ;\n\t}\n\n}\n\nclass  EncryptedReaderDecorator  implements  Reader{\n\n\tlate  Reader  wrapee ;\n\tEncryptedReaderDecorator(this.wrapee);\n\n\t@override\n\tString  read() {\n\t\treturn  _decode(wrapee.read()) ;\n\t}\n\n\t@override\n\tvoid  write(String  value) {\n\t\twrapee.write(_encode(value));\n\t}\n  \n\tString  _decode(String  value){\n\t\treturn  value.replaceAll(\"encode\", \"decoded\") ; //for demo only\n\t}\n\n\tString  _encode(String  value){\n\t\treturn  \"${value}encode\" ; //for demo only\n\t}\n}\n\nclass  TranslatedReaderDecorator  implements  Reader{\n\n\tlate  Reader  wrapee ;\n\tTranslatedReaderDecorator(this.wrapee);\n\n\t@override\n\tString  read() {\n\t\treturn  _translate(wrapee.read()) ;\n\t}\n\n\t@override\n\tvoid  write(String  value) {\n\t\twrapee.write(_translate(value));\n\t}\n\n\tString  _translate(String  value){\n\t\treturn  value.replaceAll(\"a\", \"أ\") ; //for demo only\n\t}\n}\n\nvoid  main(){\n\n\tEncryptedReaderDecorator  encryptedFile  = EncryptedReaderDecorator(FileReader());\n\tEncryptedReaderDecorator  encryptedText  =  EncryptedReaderDecorator(TextReader());\n\tEncryptedReaderDecorator  encryptedImage  =  EncryptedReaderDecorator(ImageReader());\n\n\tTranslatedReaderDecorator  translatedFile  =  TranslatedReaderDecorator(FileReader());\n\tTranslatedReaderDecorator  translatedText  =  TranslatedReaderDecorator(TextReader());\n\tTranslatedReaderDecorator  translatedImage  =  TranslatedReaderDecorator(ImageReader());\n\n\tencryptedFile.write(\"Test \");\n\tencryptedText.write(\"Test \");\n\tencryptedImage.write(\"Test \");\n\n\tprint(encryptedFile.read());\n\tprint(encryptedText.read());\n\tprint(encryptedImage.read());\n\t\n\ttranslatedFile.write(\"Aaa \");\n\ttranslatedText.write(\"Aaa \");\n\ttranslatedImage.write(\"Aaa \");\n\n\tprint(translatedFile.read());\n\tprint(translatedText.read());\n\tprint(translatedImage.read());\n\t\n\t//Try to add new Varient\n}\n```\n  \n\n  \n\n  \n\nOutput:\n```\nTest decoded File\n\nTest decoded Text\n\nTest decoded image\n\nAaa File\n\nAaa Text\n\nAأأ imaأge\n```\n  \n  \n\n### 3.3 Facade Design Patterns\n\nThe facade design pattern is to use a class that hides the complexities of initialization and features of different classes and provides a limited interface with limited functionality (a bit required) compared with the original one.\n\n  ```dart\n//Video Conversion is done through the following steps:\n// 1- read the input file using suitable codec\n// 2- play the input video\n// 3- read the video with the desired codec\n// 4- fix the audio\n// 5 - return the video file\n\nclass  VideoFile {\n\tlate  String  name ;\n\tlate  String  codec ;\n\tVideoFile(this.name, this.codec);\n}\n\nclass  VideoCodec {\n\tlate  String  type ;\n}\n\nclass  Mp4Codec  implements  VideoCodec {\n\t@override\n\tString  type  =  \"Mp4\";\n}\n\n  \n\nclass  AVICodec  implements  VideoCodec {\n\t@override\n\tString  type  =  \"avi\";\n}  \n\n//use factory design pattern\nclass  CodecFactoty {\n\tstatic  VideoCodec  createCodec(String  codecType){\n\n\t\tif (codecType  ==  \"mp4\"){\n\t\t\treturn  Mp4Codec();\n\t\t}else  if (codecType  ==  \"avi\"){\n\t\t\treturn  AVICodec();\n\t\t}else{\n\t\t\tthrow  UnsupportedError(\"no video codec is avaliable for this type ${codecType}\");\n\t\t}\n\t}\n}\n\n//class that do some complex stuff\nclass  BitRateReader{\n\n\tstatic  VideoFile  readVideo(VideoFile  inputFile, VideoCodec  inputCodec){\n\tprint(\"reading file ${inputFile.name} with input codec ${inputCodec.type}\" );\n\treturn  inputFile;\n\t}\n\t\n\tstatic  VideoFile  writeVideo(VideoFile  inputFile, VideoCodec  outputCoding){\n\t\tprint(\"writing file ${inputFile.name} with output codec ${outputCoding.type}\" );\n\t\treturn  inputFile ;\n\t}\n}\n\n//class that do some complex stuff\nclass  AudioMixer {\n\n\tstatic  VideoFile  fix(VideoFile  result){\n\t\tprint(\"AudioMixer: fixing audio...\");\n\t\treturn  result;\n\t}\n\n}\n\n//hide all complexities of video conversion\n\nclass  VideoConversionFacade {\n\tVideoFile  convertFile(VideoFile  file, String  output){\n\t\t\n\t\tVideoCodec  inputCodec  =  CodecFactoty.createCodec(file.codec);\n\t\tVideoCodec  outputCodec  =  CodecFactoty.createCodec(output);\n\t\tVideoFile  intermediateVideo  =  BitRateReader.readVideo(file, inputCodec);\n\t\tVideoFile  outputVideoFile  =  BitRateReader.writeVideo(intermediateVideo, outputCodec);\n\t\tVideoFile  result  =  AudioMixer.fix(outputVideoFile);\n\t\treturn  result;\n\n\t}\n}\n\nvoid  main(){\n\n//here we will use only two functions that hides all of these complexities\nVideoFile  inputFile  =  VideoFile(\"file\", \"mp4\");\n\n//with facade\nVideoFile  convertedFile  =  VideoConversionFacade().convertFile(inputFile, \"avi\");\n\n//without Facade\nVideoCodec  inputCodec  =  CodecFactoty.createCodec(inputFile.codec);\nVideoCodec  outputCodec  =  CodecFactoty.createCodec(\"avi\");\nVideoFile  intermediateVideo  =  BitRateReader.readVideo(convertedFile, inputCodec);\nVideoFile  outputVideoFile  =  BitRateReader.writeVideo(intermediateVideo, outputCodec);\nVideoFile  result  =  AudioMixer.fix(outputVideoFile);\n\n// the client should know all of these step and use them in correct order\n}\n```\n  \n\n## 4. Behavioral Design Patterns\n\nBehavioral design patterns concern algorithms and assigning responsibilities between objects. Unlike structural patterns, the Behavioral pattern doesn’t group many objects into a single one it just defines the rules of communication between objects.\n\n### 4.1 Observer Design Patterns\n\n  \n\nA behavioral pattern that is used when some objects need to be updated when other objects are updated (a many-to-many relationship), it is mainly used in GUI applications like setState in StatefulWidget in Flutter framework.\n\n  \n\nThere are two types of objects in the Observer mechanism, Publisher and subscriber; a publisher is the object that notifies its subscribers when its state is changed.\n\n  \n\nIn this example, a many-to-many relationship between Publishers and Subscribers is used.\n\n![](https://lh7-us.googleusercontent.com/docsz/AD_4nXckR4Pi4ErZGlnDLvBhQ3XU9XHYMxZDBgYr43UdNpS3Jj5jX4SpkyyIKw_CY7TcxBBND7HB-rFL6J7DXxgJQbHuwRRupng9wueJC-XFiN_CKhDjOFPBywC1UItfL2ooCfLRwopOr0tos-qTusUCwRDvt62G?key=ez7LbFDoMbwacBlAAOF7Qg)\n\n  \n**For publishers**:\n\nA publisher should maintain a list of subscribers and a function that calls their update function (of Subscriber) when the client needs to do so. So for publisher interface should contain a subscriber list and notify listeners function and the attach observer function.\n\n\nThe implementation for the previous functions is the same for every sub-class so I removed the implementation to a separate concrete class and made every new publisher extend it.\n  \n```dart\n//publishers\nimport  'subscribers.dart';\n\nabstract  class  PublisherInterface {\n\tabstract  List\u003cSubscriberInterface\u003e  subscribers ;\n\tvoid  attachListener(SubscriberInterface  subscriber);\n\tvoid  notifyListeners(var  val);\n}\n\n//we can split the publisher logic to concrete abstract class and\n//allow every subclass to extend its logic\n\nclass  Publisher  implements  PublisherInterface {\n\t@override\n\tList\u003cSubscriberInterface\u003e  subscribers  = [];\n\n\t@override\n\tvoid  attachListener(SubscriberInterface  subscriber) {\n\t\tsubscribers.add(subscriber);\n\t}\n\n\t@override\n\tvoid  notifyListeners(var  value) {\n\t\tfor (var  subscriber  in  subscribers) {\n\t\t\tsubscriber.update(value);\n\t\t}\n\t}\n}\n\nclass  ConcretePublisher  extends  Publisher {\n\t@override\n\tList\u003cSubscriberInterface\u003e  subscribers  = [];\n\tvar  watchedVariableInt  =  10 ;\n\tvar  watchedVariableString  =  \"Value\" ;\n\t\n\tvoid  updateIntState(var  _watchedVariable){\n\t\twatchedVariableInt  =  _watchedVariable;\n\t\tnotifyListeners(_watchedVariable);\n\t}\n\n\tvoid  updateStringState(var  _watchedVariable){\n\t\twatchedVariableString  =  _watchedVariable;\n\t\tnotifyListeners(_watchedVariable);\n\t}\n\n}\n```\n\n**For Subscribers**:\n\nEvery subscriber should have a different implementation in case of notification that a state is changed so a subscriber interface should contain an update function.\n\n```dart\n//subscribers\nimport  'publishers.dart';\n\nabstract  class  SubscriberInterface {\n\tvoid  update(var  val);\n}\n\nclass  Subscriber  implements  SubscriberInterface {\n\n\tList\u003cPublisher\u003e  publishers  = [];\n\t@override\n\tvoid  update(var  newValue) {\n\t\tprint(\"the new value is ${newValue}\");\n\t}\n\n\tvoid  registerToPublisher(Publisher  publisher) {\n\t\tpublisher.attachListener(this);\n\t}\n}\n```\n  \n**For Client Code**:\nI used many publishers with many subscribers\n\n```dart\n//Observer Example\nimport  'publishers.dart';\nimport  'subscribers.dart';\n\nvoid  main(){\n\tConcretePublisher  publisher1  =  ConcretePublisher();\n\tConcretePublisher  publisher2  =  ConcretePublisher();\n\n\tSubscriber  subscriber1  =  Subscriber();\n\tSubscriber  subscriber2  =  Subscriber();\n\tSubscriber  subscriber3  =  Subscriber();\n  \n\tsubscriber1.registerToPublisher(publisher1);\n\tsubscriber1.registerToPublisher(publisher2);\n\t\n\tsubscriber2.registerToPublisher(publisher1);\n\tsubscriber3.registerToPublisher(publisher2);\n\n\tpublisher1.updateIntState(30);\n\tpublisher2.updateStringState(\"Some String\");\n}\n```\n  \n```\nthe new value is 30\nthe new value is 30\nthe new value is Some String\nthe new value is Some String\n```\n  \n\n### 4.2 Strategy Design Patterns\n\nWhen you have a family of algorithms for a certain task that changes by changing the environment (e.g. picking an image from Android isn’t like picking an image from Windows in Flutter) you can make a bulk of switch cases that degrade the code for every new update. Also if you are not sure which algorithm should be used in runtime (e.g. imagine you have a calculator application and you want the user to select a certain operation every operation has its own algorithm) you can use a strategy design pattern.\n\nThe strategy design pattern suggests splitting the algorithms into separate classes and passing objects from these classes into the main class.\n\n![block diagram](https://i.ibb.co/T88y6tc/Capture.png)  \n\n\nIn this example: in flutter, the File Picker widget returns different file objects for each platform and every object requires a different handling algorithm. Here we will demonstrate only the concept without the real implementation of handling.\n\nWe use a factory class as an application for the multiple classes and as another example for this design pattern.\n\n  \n\n**Image File Classes and its factory**:\n```dart\n//use factory design pattern for handling different Image Files classes on different OSs\nabstract  class  ImageFile{\n\tabstract  String  type ;\n}\n\nclass  AndroidImageFile  implements  ImageFile{\n\t@override\n\tString  type  =  \"android\";\n}\n\nclass  IosImageFile  implements  ImageFile{\n\t@override\n\tString  type  =  \"ios\";\n}\n\nclass  WindowsImageFile  implements  ImageFile{\n\t@override\n\tString  type  =  \"windows\";\n}\n\nclass  MacImageFile  implements  ImageFile{\n\t@override\n\tString  type  =  \"mac\";\n}\n\nclass  WebImageFile  implements  ImageFile{\n\t@override\n\tString  type  =  \"web\";\n}\n\nclass  ImageFileFactory {\n\n\tImageFile  createImageFile(String  platform){\n\t\tswitch (platform) {\n\t\t\tcase  \"android\":\n\t\t\t\treturn  AndroidImageFile();\n\t\t\tcase  \"ios\":\n\t\t\t\treturn  IosImageFile();\n\t\t\tcase  \"web\":\n\t\t\t\treturn  WebImageFile();\n\t\t\tcase  \"windows\":\n\t\t\t\treturn  WindowsImageFile();\n\t\t\tcase  \"mac\":\n\t\t\t\treturn  MacImageFile();\n\t\t\tdefault:\n\t\t\t\tthrow  Exception(\"platform isn't supported\");\n\t\t}\n\n\t}\n}\n```\n\n**ImagePicker Strategy example and Client Code**:\n```dart\n//demo for picking image from different OS\n//Android, ios, windows, mac, linux, web\nimport  'image_file_factory.dart';\n\nabstract  class  PickImageStrategy {\n\tImageFile  pickImage();\n}\n\n//implement different algorithm for each class\nclass  PickAndroidImage  implements  PickImageStrategy {\n\n\t@override\n\tImageFile  pickImage() {\n\t\tprint(\"android image\");\n\t\treturn  AndroidImageFile();\n\t}\n}\n\nclass  PickIosImage  implements  PickImageStrategy {\n\n\t@override\n\tImageFile  pickImage() {\n\t\tprint(\"ios image\");\n\t\treturn  IosImageFile();\n\t}\n\n}\n\nclass  PickWindowsImage  implements  PickImageStrategy {\n\n\t@override\n\tImageFile  pickImage() {\n\t\tprint(\"windows image\");\n\t\treturn  WindowsImageFile();\n\t}\n}\n\nclass  PickMacImage  implements  PickImageStrategy {\n\n\t@override\n\tImageFile  pickImage() {\n\t\tprint(\"mac image\");\n\t\treturn  MacImageFile();\n\t}\n\n}\n\nclass  PickWebImage  implements  PickImageStrategy {\n\n\t@override\n\tImageFile  pickImage() {\n\t\tprint(\"web image\");\n\t\treturn  WebImageFile();\n\t}\n\n}\n\nclass  ImagePicker {\n\n\tImageFile  pickImage(PickImageStrategy  strategy) {\n\t\treturn  strategy.pickImage();\n\t}\n\n}\n\nvoid  main(){\n\tImagePicker  imagePicker  =  ImagePicker();\n\timagePicker.pickImage(PickAndroidImage());\n}\n```\n  \n```\nandroid image\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fziadasem%2Fdesign-patterns-in-dart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fziadasem%2Fdesign-patterns-in-dart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fziadasem%2Fdesign-patterns-in-dart/lists"}