├── patterns ├── builder │ ├── conceptual │ │ ├── pattern │ │ │ ├── product.dart │ │ │ ├── builder.dart │ │ │ └── director.dart │ │ ├── main.dart │ │ ├── product │ │ │ ├── concrete_product_2.dart │ │ │ └── concrete_product_1.dart │ │ ├── concrete_builder │ │ │ ├── concrete_builder_1.dart │ │ │ └── concrete_builder_2.dart │ │ └── README.md │ ├── cars │ │ ├── cars │ │ │ ├── car_type.dart │ │ │ └── car.dart │ │ ├── director │ │ │ ├── transmission.dart │ │ │ ├── gps_navigation.dart │ │ │ └── trip_computer.dart │ │ ├── README.md │ │ ├── components │ │ │ └── engine.dart │ │ └── builders │ │ │ ├── builder.dart │ │ │ └── car_builder.dart │ └── color_text_format │ │ ├── formats │ │ ├── text_format.dart │ │ ├── json_format.dart │ │ └── console_format.dart │ │ ├── converters │ │ ├── converter.dart │ │ ├── console_converter.dart │ │ ├── html_converter.dart │ │ └── json_converter.dart │ │ ├── color_reader │ │ └── color_text_reader.dart │ │ └── main.dart ├── observer │ ├── app_observer │ │ ├── observer │ │ │ └── event.dart │ │ └── main.dart │ ├── open_close_editor_events │ │ ├── listeners │ │ │ ├── event_listener.dart │ │ │ ├── email_notification_listener.dart │ │ │ └── log_open_listener.dart │ │ ├── editor │ │ │ └── editor.dart │ │ ├── main.dart │ │ └── event_manager │ │ │ └── event_manager.dart │ └── subscriber_flutter_widget │ │ ├── events │ │ └── new_hash_event.dart │ │ ├── widgets │ │ ├── hash_generator_widget.dart │ │ └── hash_user_widget.dart │ │ ├── main.dart │ │ └── subscriber │ │ └── subscriber_widget.dart ├── facade │ └── conceptual │ │ ├── some_complex_media_library │ │ ├── codec.dart │ │ ├── ogg_compression_codec.dart │ │ ├── mpeg4_compression_codec.dart │ │ ├── video_file.dart │ │ ├── audio_mixer.dart │ │ ├── bitrate_reader.dart │ │ └── codec_factory.dart │ │ ├── main.dart │ │ └── README.md ├── memento │ ├── memento_editor │ │ ├── memento_pattern │ │ │ ├── snapshot.dart │ │ │ ├── memento.dart │ │ │ └── caretaker.dart │ │ ├── editor │ │ │ ├── memento_create_event.dart │ │ │ ├── manipulator.dart │ │ │ └── editor.dart │ │ ├── widgets │ │ │ ├── composite │ │ │ │ └── named_panel.dart │ │ │ └── right_panel_widget.dart │ │ ├── main.dart │ │ ├── shapes │ │ │ └── shape.dart │ │ └── application.dart │ └── conceptual │ │ ├── main.dart │ │ └── app │ │ ├── command.dart │ │ ├── snapshot.dart │ │ └── editor.dart ├── proxy │ └── conceptual │ │ ├── pattern │ │ ├── subject.dart │ │ ├── real_subject.dart │ │ └── proxy.dart │ │ ├── main.dart │ │ └── README.md ├── abstract_factory │ ├── conceptual_gui_factory │ │ ├── button │ │ │ ├── button.dart │ │ │ ├── mac_os_button.dart │ │ │ └── windows_button.dart │ │ ├── checkbox │ │ │ ├── checkbox.dart │ │ │ ├── mac_os_checkbox.dart │ │ │ └── windows_checkbox.dart │ │ ├── main.dart │ │ ├── factories │ │ │ ├── mac_os_factory.dart │ │ │ ├── window_factory.dart │ │ │ └── gui_factory.dart │ │ ├── app │ │ │ └── application.dart │ │ └── README.md │ └── tool_panel_factory │ │ ├── pattern │ │ ├── property.dart │ │ ├── shape.dart │ │ └── tool_factory.dart │ │ ├── widgets │ │ ├── property_widgets │ │ │ ├── factories │ │ │ │ ├── property_widget_factory.dart │ │ │ │ └── property_widget_factories.dart │ │ │ └── primitive │ │ │ │ ├── filed_label.dart │ │ │ │ └── theme_property.dart │ │ ├── tool_panel.dart │ │ ├── colors_tool_bar.dart │ │ └── independent │ │ │ ├── panel.dart │ │ │ ├── tool_button.dart │ │ │ ├── tool_bar.dart │ │ │ ├── event_listenable_builder.dart │ │ │ └── hove.dart │ │ ├── app │ │ ├── app.dart │ │ ├── shapes.dart │ │ └── tools.dart │ │ ├── shapes │ │ ├── base_shape.dart │ │ ├── circle_shape.dart │ │ ├── line_shape.dart │ │ └── triangle_shape.dart │ │ └── factories │ │ ├── line_factory.dart │ │ ├── star_factory.dart │ │ ├── text_factory.dart │ │ ├── circle_factory.dart │ │ └── triangle_factory.dart ├── bridge │ ├── clock │ │ ├── bells │ │ │ ├── bell.dart │ │ │ ├── melody.dart │ │ │ └── signal.dart │ │ ├── clocks │ │ │ ├── clock.dart │ │ │ ├── once.dart │ │ │ └── interval.dart │ │ ├── main.dart │ │ └── README.md │ └── devices_remote_control │ │ ├── remotes │ │ └── remote.dart │ │ ├── devices │ │ ├── device.dart │ │ ├── empty_device.dart │ │ ├── tv.dart │ │ └── radio.dart │ │ └── main.dart ├── command │ ├── conceptual │ │ ├── pattern │ │ │ └── command.dart │ │ ├── mut_str │ │ │ └── mut_str.dart │ │ ├── command │ │ │ ├── add_text_command.dart │ │ │ └── insert_text_command.dart │ │ └── main.dart │ └── text_editor │ │ ├── commands │ │ ├── command.dart │ │ ├── copy_command.dart │ │ ├── move_command.dart │ │ ├── cut_command.dart │ │ ├── input_command.dart │ │ ├── select_command.dart │ │ └── past_command.dart │ │ ├── application │ │ ├── command_history.dart │ │ └── text_cursor.dart │ │ └── main.dart ├── iterator │ ├── github_commit │ │ ├── github │ │ │ ├── commit.dart │ │ │ └── github_loader.dart │ │ ├── pattern │ │ │ └── github_repo.dart │ │ └── main.dart │ └── word_iterator │ │ ├── text │ │ └── text.dart │ │ ├── main.dart │ │ ├── README.md │ │ └── pattern │ │ └── word_iterator.dart ├── template_method │ └── data_miner │ │ ├── utils │ │ ├── raw_data.dart │ │ ├── formatted_table.dart │ │ └── analysis.dart │ │ ├── reports │ │ ├── refactoring_guru_workers.zip │ │ └── twitter_workers.csv │ │ ├── miners │ │ ├── csv_miner.dart │ │ ├── html_analyzer.dart │ │ └── zip_miner.dart │ │ └── main.dart ├── adapter │ ├── flutter_adapter │ │ ├── classic_app │ │ │ ├── repaint_compatible.dart │ │ │ ├── repaint_event.dart │ │ │ └── classic_app.dart │ │ ├── client_app │ │ │ ├── business_rules │ │ │ │ ├── color_rules.dart │ │ │ │ └── text_coloring.dart │ │ │ └── widgets │ │ │ │ ├── slider_widget.dart │ │ │ │ └── color_buttons_widget.dart │ │ ├── adapter │ │ │ └── classic_app_adapter_widget.dart │ │ └── main.dart │ ├── text_graphics │ │ ├── third_party_graphics_lib │ │ │ ├── graph_element.dart │ │ │ ├── wave.dart │ │ │ └── star.dart │ │ ├── engine │ │ │ └── shape_engine.dart │ │ └── shapes │ │ │ ├── graph_element_adapter.dart │ │ │ └── shape.dart │ └── square_round_conflict │ │ └── README.md ├── strategy │ ├── reservation_cargo_spaces │ │ ├── partners │ │ │ ├── cargo.dart │ │ │ └── voyage.dart │ │ ├── application │ │ │ ├── order_confirmation_sequence.dart │ │ │ └── application.dart │ │ ├── policy │ │ │ └── overbooking_policy.dart │ │ └── main.dart │ └── view_strategy │ │ ├── pattern │ │ ├── view_strategy.dart │ │ └── byte_context.dart │ │ ├── strategies │ │ ├── str_view_strategy.dart │ │ └── zip_view_strategy.dart │ │ └── main.dart ├── visitor │ ├── conceptual │ │ ├── after │ │ │ ├── pattern │ │ │ │ ├── element.dart │ │ │ │ └── visitor.dart │ │ │ ├── elements │ │ │ │ ├── one.dart │ │ │ │ ├── two.dart │ │ │ │ └── three.dart │ │ │ ├── operations │ │ │ │ ├── concrete_visitor1.dart │ │ │ │ └── concrete_visitor2.dart │ │ │ └── main.dart │ │ └── before │ │ │ ├── elements │ │ │ └── elements.dart │ │ │ └── main.dart │ └── shapes_exporter │ │ ├── shapes │ │ ├── shape.dart │ │ ├── dot.dart │ │ ├── circle.dart │ │ ├── compound_shape.dart │ │ └── rectangle.dart │ │ ├── visitor │ │ └── visitor.dart │ │ └── main.dart ├── decorator │ └── data_source_decoder │ │ ├── src │ │ ├── data_source.dart │ │ ├── data_source_decorator.dart │ │ ├── file_data_source.dart │ │ ├── encryption_decorator.dart │ │ └── compression_decorator.dart │ │ ├── Secret.txt │ │ └── main.dart ├── mediator │ └── conceptual │ │ ├── pattern │ │ ├── component.dart │ │ └── mediator.dart │ │ ├── components │ │ ├── component1.dart │ │ └── component2.dart │ │ ├── main.dart │ │ ├── concrete_mediator │ │ └── concrete_mediator.dart │ │ └── README.md ├── interpreter │ └── conceptual │ │ ├── pattern │ │ ├── expression.dart │ │ └── context.dart │ │ ├── operations │ │ ├── and.dart │ │ ├── or.dart │ │ ├── xor.dart │ │ └── operation.dart │ │ ├── variable │ │ └── bool_variable.dart │ │ ├── main.dart │ │ └── README.md ├── state │ ├── three_state │ │ ├── pattern │ │ │ ├── state.dart │ │ │ └── switcher.dart │ │ ├── states │ │ │ ├── one.dart │ │ │ ├── three.dart │ │ │ └── two.dart │ │ ├── main.dart │ │ └── README.md │ └── manipulator_state │ │ ├── app │ │ ├── tool.dart │ │ ├── app.dart │ │ └── shapes.dart │ │ ├── states │ │ ├── creations │ │ │ ├── text_creation_state.dart │ │ │ ├── circle_creation_state.dart │ │ │ └── rectangle_creation_state.dart │ │ ├── selections │ │ │ ├── text │ │ │ │ ├── text_cursor_animation.dart │ │ │ │ ├── keyboard_actions.dart │ │ │ │ └── text_size_marker_state.dart │ │ │ ├── inner_radius_state.dart │ │ │ ├── resizable_markers │ │ │ │ ├── bottom_right_marker_state.dart │ │ │ │ ├── top_left_marker_state.dart │ │ │ │ ├── bottom_left_marker_state.dart │ │ │ │ └── top_right_marker_state.dart │ │ │ ├── move_state.dart │ │ │ ├── text_resize_state.dart │ │ │ ├── selection_state.dart │ │ │ ├── resizable_state.dart │ │ │ └── inner_radius_markers │ │ │ │ └── inner_radius_marker_state.dart │ │ ├── free_sate.dart │ │ └── _ │ │ │ ├── mixins │ │ │ └── hover_shape_mixin.dart │ │ │ └── marker.dart │ │ ├── shapes │ │ ├── shape.dart │ │ ├── marker_shape.dart │ │ ├── rectangle_shape.dart │ │ └── base_shape.dart │ │ └── pattern │ │ ├── manipulation_state.dart │ │ └── manipulator.dart ├── flyweight │ └── conceptual │ │ ├── params │ │ └── share_params.dart │ │ ├── utils │ │ └── fake_value.dart │ │ ├── pattern │ │ ├── flyweight.dart │ │ └── flyweight_factory.dart │ │ └── main.dart ├── composite │ ├── products_and_boxes │ │ ├── diagram │ │ │ ├── dist │ │ │ │ └── _03_trunk-8daed00e4b15a9cd_bg.wasm │ │ │ └── convert_product_to_render_element.dart │ │ ├── products │ │ │ ├── product.dart │ │ │ ├── product_leaf.dart │ │ │ └── box.dart │ │ ├── render_elements │ │ │ ├── render_element.dart │ │ │ ├── render_text.dart │ │ │ ├── render_position.dart │ │ │ ├── render_row.dart │ │ │ ├── render_column.dart │ │ │ └── render_layout.dart │ │ └── main.dart │ └── image_editor │ │ ├── shapes │ │ ├── shape.dart │ │ ├── dot.dart │ │ ├── rectangle.dart │ │ ├── circle.dart │ │ └── base_shape.dart │ │ ├── editor │ │ └── image_editor.dart │ │ └── main.dart ├── singleton │ └── conceptual │ │ ├── main.dart │ │ ├── pattern │ │ └── singleton.dart │ │ └── README.md ├── factory_method │ └── conceptual_platform_dialog │ │ ├── buttons │ │ ├── button.dart │ │ ├── html_button.dart │ │ └── windows_button.dart │ │ ├── dialogs_factory │ │ ├── html_dialog.dart │ │ ├── windows_dialog.dart │ │ └── dialog.dart │ │ ├── main.dart │ │ └── README.md ├── prototype │ └── shapes │ │ ├── shape │ │ ├── shape.dart │ │ ├── rectangle.dart │ │ └── circle.dart │ │ └── main.dart └── chain_of_responsibility │ └── server_middleware │ ├── server │ ├── middleware.dart │ └── server.dart │ └── middleware │ ├── role_check_middleware.dart │ └── user_exists_middleware.dart ├── .gitignore ├── web ├── favicon.png ├── logo-screen.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── logo-screen-ukraine.png └── manifest.json ├── lib └── text_canvas.dart ├── pubspec.yaml └── analysis_options.yaml /patterns/builder/conceptual/pattern/product.dart: -------------------------------------------------------------------------------- 1 | abstract class Product {} 2 | -------------------------------------------------------------------------------- /patterns/observer/app_observer/observer/event.dart: -------------------------------------------------------------------------------- 1 | abstract class Event { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/some_complex_media_library/codec.dart: -------------------------------------------------------------------------------- 1 | abstract class Codec {} 2 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/memento_pattern/snapshot.dart: -------------------------------------------------------------------------------- 1 | typedef Snapshot = String; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dart_tool/ 2 | .idea/ 3 | build/ 4 | windows/ 5 | 6 | .packages 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/web/favicon.png -------------------------------------------------------------------------------- /patterns/builder/cars/cars/car_type.dart: -------------------------------------------------------------------------------- 1 | enum CarType { 2 | cityCar, 3 | sportCar, 4 | suv, 5 | } 6 | -------------------------------------------------------------------------------- /patterns/proxy/conceptual/pattern/subject.dart: -------------------------------------------------------------------------------- 1 | abstract class Subject { 2 | String request(); 3 | } 4 | -------------------------------------------------------------------------------- /web/logo-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/web/logo-screen.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/button/button.dart: -------------------------------------------------------------------------------- 1 | abstract class Button { 2 | void paint(); 3 | } 4 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/checkbox/checkbox.dart: -------------------------------------------------------------------------------- 1 | abstract class Checkbox { 2 | void paint(); 3 | } 4 | -------------------------------------------------------------------------------- /patterns/bridge/clock/bells/bell.dart: -------------------------------------------------------------------------------- 1 | abstract class Bell { 2 | void ring(); 3 | 4 | void notify(String message); 5 | } 6 | -------------------------------------------------------------------------------- /patterns/command/conceptual/pattern/command.dart: -------------------------------------------------------------------------------- 1 | abstract class Command { 2 | void execute(); 3 | void undo(); 4 | } 5 | -------------------------------------------------------------------------------- /web/logo-screen-ukraine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/web/logo-screen-ukraine.png -------------------------------------------------------------------------------- /patterns/iterator/github_commit/github/commit.dart: -------------------------------------------------------------------------------- 1 | class Commit { 2 | String message; 3 | 4 | Commit(this.message); 5 | } 6 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/utils/raw_data.dart: -------------------------------------------------------------------------------- 1 | typedef RawData = String; 2 | typedef StringTable = List>; 3 | -------------------------------------------------------------------------------- /lib/text_canvas.dart: -------------------------------------------------------------------------------- 1 | library design_pttern_dart; 2 | 3 | export 'text_canvas/canvas.dart'; 4 | export 'text_canvas/primitives.dart'; 5 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/classic_app/repaint_compatible.dart: -------------------------------------------------------------------------------- 1 | abstract class RepaintCompatible { 2 | void repaint(); 3 | } 4 | -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /patterns/strategy/reservation_cargo_spaces/partners/cargo.dart: -------------------------------------------------------------------------------- 1 | class Cargo { 2 | final double size; 3 | 4 | Cargo(this.size); 5 | } 6 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/pattern/element.dart: -------------------------------------------------------------------------------- 1 | import 'visitor.dart'; 2 | 3 | abstract class Element { 4 | void accept(Visitor visitor); 5 | } 6 | -------------------------------------------------------------------------------- /patterns/decorator/data_source_decoder/src/data_source.dart: -------------------------------------------------------------------------------- 1 | abstract class DataSource { 2 | void writeData(String data); 3 | 4 | String readData(); 5 | } 6 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/classic_app/repaint_event.dart: -------------------------------------------------------------------------------- 1 | import '../../../observer/app_observer/observer/event.dart'; 2 | 3 | class RepaintEvent extends Event {} 4 | -------------------------------------------------------------------------------- /patterns/decorator/data_source_decoder/Secret.txt: -------------------------------------------------------------------------------- 1 | STV0SkJCQkJCQkJCRHdPTXtGNFdEVjhOVFR6cjZRTUx7OWlVRE44T01Ob1JOVVJCQmI4aGx1VHoyRXhHczB6bFppMk1SelBoRkJCOHhaTzlOQkJCQkI+Pg== -------------------------------------------------------------------------------- /patterns/mediator/conceptual/pattern/component.dart: -------------------------------------------------------------------------------- 1 | part of mediator; 2 | 3 | class Component { 4 | get mediator => _mediator; 5 | 6 | Mediator? _mediator; 7 | } 8 | -------------------------------------------------------------------------------- /patterns/adapter/text_graphics/third_party_graphics_lib/graph_element.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | abstract class GraphElement { 4 | Int32List get points; 5 | } 6 | -------------------------------------------------------------------------------- /patterns/strategy/view_strategy/pattern/view_strategy.dart: -------------------------------------------------------------------------------- 1 | import 'byte_context.dart'; 2 | 3 | abstract class ViewStrategy { 4 | String out(ByteContext byteList); 5 | } 6 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/editor/memento_create_event.dart: -------------------------------------------------------------------------------- 1 | import '../../../observer/app_observer/observer/event.dart'; 2 | 3 | class MementoCreateEvent extends Event {} 4 | -------------------------------------------------------------------------------- /patterns/observer/open_close_editor_events/listeners/event_listener.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | abstract class EventListener { 4 | void update(String eventType, File file); 5 | } 6 | -------------------------------------------------------------------------------- /patterns/strategy/reservation_cargo_spaces/application/order_confirmation_sequence.dart: -------------------------------------------------------------------------------- 1 | class OrderConfirmationSequence { 2 | var _index = 0; 3 | 4 | int next() => _index++; 5 | } 6 | -------------------------------------------------------------------------------- /patterns/visitor/shapes_exporter/shapes/shape.dart: -------------------------------------------------------------------------------- 1 | import '../visitor/visitor.dart'; 2 | 3 | abstract class Shape { 4 | void accept(Visitor visitor); 5 | 6 | void draw(); 7 | } 8 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/some_complex_media_library/ogg_compression_codec.dart: -------------------------------------------------------------------------------- 1 | import 'codec.dart'; 2 | 3 | class OggCompressionCodec implements Codec { 4 | final type = 'ogg'; 5 | } 6 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/some_complex_media_library/mpeg4_compression_codec.dart: -------------------------------------------------------------------------------- 1 | import 'codec.dart'; 2 | 3 | class MPEG4CompressionCodec implements Codec { 4 | String type = 'mp4'; 5 | } 6 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/pattern/expression.dart: -------------------------------------------------------------------------------- 1 | import 'context.dart'; 2 | 3 | abstract class Expression { 4 | bool evaluate(Context context); 5 | 6 | String toDebugString(Context context); 7 | } 8 | -------------------------------------------------------------------------------- /patterns/proxy/conceptual/pattern/real_subject.dart: -------------------------------------------------------------------------------- 1 | import 'subject.dart'; 2 | 3 | class RealSubject implements Subject { 4 | @override 5 | String request() { 6 | return 'Real data.'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /patterns/state/three_state/pattern/state.dart: -------------------------------------------------------------------------------- 1 | part of switcher; 2 | 3 | abstract class State { 4 | Switcher get context => _context; 5 | 6 | void call(); 7 | 8 | late Switcher _context; 9 | } 10 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/reports/refactoring_guru_workers.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/patterns/template_method/data_miner/reports/refactoring_guru_workers.zip -------------------------------------------------------------------------------- /patterns/flyweight/conceptual/params/share_params.dart: -------------------------------------------------------------------------------- 1 | class ShareParams { 2 | final int param1; 3 | final String param2; 4 | final double param3; 5 | 6 | ShareParams(this.param1, this.param2, this.param3); 7 | } 8 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/pattern/builder.dart: -------------------------------------------------------------------------------- 1 | import 'product.dart'; 2 | 3 | abstract class Builder { 4 | void buildPart1(); 5 | void buildPart2(); 6 | void buildPart3(); 7 | 8 | Product get result; 9 | } 10 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/some_complex_media_library/video_file.dart: -------------------------------------------------------------------------------- 1 | class VideoFile { 2 | final String name; 3 | final String codecType; 4 | 5 | VideoFile(this.name) : codecType = name.substring(name.indexOf('.') + 1); 6 | } 7 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/memento_pattern/memento.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'snapshot.dart'; 3 | 4 | class Memento { 5 | final DateTime time; 6 | final Snapshot snapshot; 7 | 8 | Memento(this.time, this.snapshot); 9 | } 10 | -------------------------------------------------------------------------------- /patterns/bridge/devices_remote_control/remotes/remote.dart: -------------------------------------------------------------------------------- 1 | abstract class Remote { 2 | void power(); 3 | 4 | void volumeDown(); 5 | 6 | void volumeUp(); 7 | 8 | void channelDown(); 9 | 10 | void channelUp(); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/diagram/dist/_03_trunk-8daed00e4b15a9cd_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RefactoringGuru/design-patterns-dart/HEAD/patterns/composite/products_and_boxes/diagram/dist/_03_trunk-8daed00e4b15a9cd_bg.wasm -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/operations/and.dart: -------------------------------------------------------------------------------- 1 | import 'operation.dart'; 2 | 3 | class And extends Operation { 4 | And(super.expression1, super.expression2); 5 | 6 | @override 7 | bool operation(bool a, bool b) => a && b; 8 | } 9 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/operations/or.dart: -------------------------------------------------------------------------------- 1 | import 'operation.dart'; 2 | 3 | class Or extends Operation { 4 | Or(super.expression1, super.expression2); 5 | 6 | @override 7 | bool operation(bool a, bool b) => a || b; 8 | } 9 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/operations/xor.dart: -------------------------------------------------------------------------------- 1 | import 'operation.dart'; 2 | 3 | class Xor extends Operation { 4 | Xor(super.expression1, super.expression2); 5 | 6 | @override 7 | bool operation(bool a, bool b) => a ^ b; 8 | } 9 | -------------------------------------------------------------------------------- /patterns/observer/subscriber_flutter_widget/events/new_hash_event.dart: -------------------------------------------------------------------------------- 1 | import '../../app_observer/observer/event.dart'; 2 | 3 | class NewHashEvent extends Event { 4 | final String newHash; 5 | 6 | NewHashEvent(this.newHash); 7 | } 8 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/button/mac_os_button.dart: -------------------------------------------------------------------------------- 1 | import 'button.dart'; 2 | 3 | class MacOSButton implements Button { 4 | @override 5 | void paint() { 6 | print('You have created MacOSButton.'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /patterns/builder/cars/director/transmission.dart: -------------------------------------------------------------------------------- 1 | /// EN: Just another feature of a car. 2 | /// 3 | /// RU: Одна из фишек автомобиля. 4 | enum Transmission { 5 | singleSpeed, 6 | manual, 7 | automatic, 8 | semiAutomatic, 9 | } 10 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/before/elements/elements.dart: -------------------------------------------------------------------------------- 1 | class One { 2 | final String param1 = '1'; 3 | } 4 | 5 | class Two { 6 | final String param2 = '2'; 7 | } 8 | 9 | class Three { 10 | final String param3 = '3'; 11 | } 12 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/button/windows_button.dart: -------------------------------------------------------------------------------- 1 | import 'button.dart'; 2 | 3 | class WindowsButton implements Button { 4 | @override 5 | void paint() { 6 | print('You have created WindowsButton.'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/formats/text_format.dart: -------------------------------------------------------------------------------- 1 | /// Product 2 | abstract class TextFormat { 3 | String get content; 4 | 5 | @override 6 | String toString() => '$runtimeType(\n' 7 | '$content' 8 | '\n)'; 9 | } 10 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/checkbox/mac_os_checkbox.dart: -------------------------------------------------------------------------------- 1 | import 'checkbox.dart'; 2 | 3 | class MacOSCheckbox implements Checkbox { 4 | @override 5 | void paint() { 6 | print('You have created MacOSCheckbox.'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/products/product.dart: -------------------------------------------------------------------------------- 1 | import '../diagram/diagram.dart'; 2 | 3 | abstract class Product { 4 | String get content; 5 | 6 | int get size; 7 | 8 | int get price; 9 | 10 | Diagram toDiagram(); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/singleton/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | import 'pattern/singleton.dart'; 2 | 3 | void main() { 4 | // dart style 5 | Singleton().doSome(); 6 | Singleton().doSome(); 7 | 8 | // standard style 9 | Singleton.instance.doSome(); 10 | } 11 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/checkbox/windows_checkbox.dart: -------------------------------------------------------------------------------- 1 | import 'checkbox.dart'; 2 | 3 | class WindowsCheckbox implements Checkbox { 4 | @override 5 | void paint() { 6 | print('You have created WindowsCheckbox.'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/main.dart: -------------------------------------------------------------------------------- 1 | import 'app/application.dart'; 2 | import 'factories/gui_factory.dart'; 3 | 4 | void main() { 5 | final guiFactory = GUIFactory(); 6 | final app = Application(guiFactory); 7 | app.paint(); 8 | } 9 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/render_elements/render_element.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | abstract class RenderElement { 4 | int get width; 5 | 6 | int get height; 7 | 8 | void render(Canvas dc); 9 | } 10 | -------------------------------------------------------------------------------- /patterns/state/three_state/states/one.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/switcher.dart'; 2 | import 'two.dart'; 3 | 4 | class One extends State { 5 | @override 6 | void call() { 7 | print('call(${context.calls}): One'); 8 | context.changeState(Two()); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patterns/iterator/word_iterator/text/text.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/word_iterator.dart'; 2 | 3 | class Text extends Iterable { 4 | final String text; 5 | 6 | Text(this.text); 7 | 8 | @override 9 | Iterator get iterator => WordIterator(this); 10 | } 11 | -------------------------------------------------------------------------------- /patterns/state/three_state/states/three.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/switcher.dart'; 2 | import 'one.dart'; 3 | 4 | class Three extends State { 5 | @override 6 | void call() { 7 | print('call(${context.calls}): Three'); 8 | context.changeState(One()); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patterns/state/three_state/states/two.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/switcher.dart'; 2 | import 'three.dart'; 3 | 4 | class Two extends State { 5 | @override 6 | void call() { 7 | print('call(${context.calls}): Two'); 8 | context.changeState(Three()); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/some_complex_media_library/audio_mixer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'video_file.dart'; 4 | 5 | class AudioMixer { 6 | File fix(VideoFile result) { 7 | print('AudioMixer: fixing audio...'); 8 | return File('tmp'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/converters/converter.dart: -------------------------------------------------------------------------------- 1 | import '../formats/text_format.dart'; 2 | 3 | /// Builder 4 | abstract class Converter { 5 | void writeWord(String text); 6 | 7 | void writeColor(String color); 8 | 9 | T get result; 10 | } 11 | -------------------------------------------------------------------------------- /patterns/command/text_editor/commands/command.dart: -------------------------------------------------------------------------------- 1 | import '../application/application.dart'; 2 | 3 | abstract class Command { 4 | final Application app; 5 | 6 | Command(this.app); 7 | 8 | bool get isSaveHistory; 9 | 10 | void execute(); 11 | 12 | void undo(); 13 | } 14 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/reports/twitter_workers.csv: -------------------------------------------------------------------------------- 1 | Laylah Whitehead,22.1 2 | Karissa Arellano,2 3 | Maggie Mcmahon,12.25 4 | Ariel Bartlett,8.1 5 | Rashad Herman,5.8 6 | Rachael Hickman,5.22 7 | Tara Spence,9.48 8 | Kiera Cervantes,4 9 | Shane Robertson,9 10 | Perla Yoder,67.3 -------------------------------------------------------------------------------- /patterns/mediator/conceptual/components/component1.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/mediator.dart'; 2 | 3 | class Component1 extends Component { 4 | final sate = 'Cmp1'; 5 | 6 | void doOne() { 7 | print('call Component1.doOne()'); 8 | mediator?.notify(this, 'doOne'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patterns/mediator/conceptual/components/component2.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/mediator.dart'; 2 | 3 | class Component2 extends Component { 4 | final name = 'Two'; 5 | 6 | void doTwo() { 7 | print('call Component2.doTwo()'); 8 | mediator?.notify(this, 'doTwo'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/buttons/button.dart: -------------------------------------------------------------------------------- 1 | /// EN: Common interface for all buttons. 2 | /// 3 | /// RU: Общий интерфейс для всех продуктов. 4 | abstract class Button { 5 | final void Function() onClick; 6 | 7 | Button(this.onClick); 8 | 9 | void render(); 10 | } 11 | -------------------------------------------------------------------------------- /patterns/mediator/conceptual/pattern/mediator.dart: -------------------------------------------------------------------------------- 1 | library mediator; 2 | 3 | part 'component.dart'; 4 | 5 | abstract class Mediator { 6 | void notify(Component component, String event); 7 | 8 | void applyThisMediator(Component component) { 9 | component._mediator = this; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /patterns/bridge/clock/bells/melody.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'bell.dart'; 4 | 5 | class Melody extends Bell { 6 | @override 7 | void notify(String message) => stdout.write('\x1b[32m$message\x1B[0m'); 8 | 9 | @override 10 | void ring() => stdout.write('\x1b[32m🎵\x1B[0m'); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/bridge/clock/bells/signal.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'bell.dart'; 4 | 5 | class Signal extends Bell { 6 | @override 7 | void notify(String message) => stdout.write('\x1b[34m$message\x1B[0m'); 8 | 9 | @override 10 | void ring() => stdout.write('\x1b[34m🔉\x1B[0m'); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | import 'pattern/video_conversion_facade.dart'; 4 | 5 | void main() { 6 | final converter = VideoConversionFacade(); 7 | final mp4Video = converter.convertVideo("youtubevideo.ogg", "mp4"); 8 | // ...; 9 | } 10 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/elements/one.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/element.dart'; 2 | import '../pattern/visitor.dart'; 3 | 4 | class One implements Element { 5 | final String param1 = '1'; 6 | 7 | @override 8 | void accept(Visitor visitor) { 9 | visitor.visitOne(this); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/elements/two.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/element.dart'; 2 | import '../pattern/visitor.dart'; 3 | 4 | class Two implements Element { 5 | final String param2 = '2'; 6 | 7 | @override 8 | void accept(Visitor visitor) { 9 | visitor.visitTwo(this); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/app/tool.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../pattern/manipulator.dart'; 4 | 5 | class Tool { 6 | final Icon icon; 7 | final ManipulationState state; 8 | 9 | Tool({ 10 | required this.icon, 11 | required this.state, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/elements/three.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/element.dart'; 2 | import '../pattern/visitor.dart'; 3 | 4 | class Three implements Element { 5 | final String param3 = '3'; 6 | 7 | @override 8 | void accept(Visitor visitor) { 9 | visitor.visitThree(this); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/pattern/visitor.dart: -------------------------------------------------------------------------------- 1 | import '../elements/one.dart'; 2 | import '../elements/three.dart'; 3 | import '../elements/two.dart'; 4 | 5 | abstract class Visitor { 6 | void visitOne(One one); 7 | 8 | void visitTwo(Two two); 9 | 10 | void visitThree(Three three); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/pattern/property.dart: -------------------------------------------------------------------------------- 1 | class Property { 2 | final String name; 3 | final T Function() value; 4 | final void Function(T) onChange; 5 | 6 | Property({ 7 | required this.name, 8 | required this.value, 9 | required this.onChange, 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/pattern/director.dart: -------------------------------------------------------------------------------- 1 | import 'builder.dart'; 2 | import 'product.dart'; 3 | 4 | class Director { 5 | Product construct(Builder builder) { 6 | builder.buildPart1(); 7 | builder.buildPart2(); 8 | builder.buildPart3(); 9 | 10 | return builder.result; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /patterns/bridge/devices_remote_control/devices/device.dart: -------------------------------------------------------------------------------- 1 | abstract class Device { 2 | bool get isEnabled; 3 | 4 | set isEnabled(bool enabled); 5 | 6 | int get volume; 7 | 8 | set volume(int percent); 9 | 10 | int get channel; 11 | 12 | set channel(int channel); 13 | 14 | void printStatus(); 15 | } 16 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/pattern/shape.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | abstract class Shape { 4 | double get x; 5 | 6 | double get y; 7 | 8 | double get width; 9 | 10 | double get height; 11 | 12 | Color get color; 13 | 14 | void paint(Canvas can); 15 | 16 | void centerToFit(); 17 | } 18 | -------------------------------------------------------------------------------- /patterns/prototype/shapes/shape/shape.dart: -------------------------------------------------------------------------------- 1 | abstract class Shape { 2 | final int x; 3 | final int y; 4 | 5 | Shape({ 6 | required this.x, 7 | required this.y, 8 | }); 9 | 10 | Shape clone(); 11 | 12 | @override 13 | String toString() => '$runtimeType(address: 0x${hashCode.toRadixString(16)})'; 14 | } 15 | -------------------------------------------------------------------------------- /patterns/strategy/view_strategy/strategies/str_view_strategy.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/byte_context.dart'; 2 | import '../pattern/view_strategy.dart'; 3 | 4 | class StrViewStrategy implements ViewStrategy { 5 | @override 6 | String out(ByteContext byteList) { 7 | return '${byteList.toList().join(', ')}\n'; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /patterns/command/text_editor/application/command_history.dart: -------------------------------------------------------------------------------- 1 | import '../commands/command.dart'; 2 | 3 | class CommandHistory { 4 | final _stack = []; 5 | 6 | bool get isNotEmpty => _stack.isNotEmpty; 7 | 8 | void push(Command command) => _stack.add(command); 9 | 10 | Command pop() => _stack.removeLast(); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/pattern/tool_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'property.dart'; 4 | import 'shape.dart'; 5 | 6 | abstract class ToolFactory { 7 | Image get icon; 8 | 9 | Shape createShape(double x, double y, Color color); 10 | 11 | Iterable get properties; 12 | } 13 | -------------------------------------------------------------------------------- /patterns/flyweight/conceptual/utils/fake_value.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | String str = 'abcdef'; 4 | get fakeString { 5 | final char = str[Random().nextInt(str.length - 1)]; 6 | str = str.replaceFirst(char, ''); 7 | return char; 8 | } 9 | 10 | get fakeInt => Random().nextInt(100); 11 | 12 | get fakeDouble => Random().nextInt(100) / 100; 13 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/miners/csv_miner.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/data_miner.dart'; 2 | import '../utils/raw_data.dart'; 3 | 4 | class CsvMiner extends DataMiner { 5 | CsvMiner(super.fileName); 6 | 7 | @override 8 | StringTable parseData(RawData raw) { 9 | return raw.split('\n').map((line) => line.split(',')).toList(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../../../pattern/property.dart'; 4 | 5 | abstract class PropertyWidgetFactory { 6 | Widget createWidget(Property property); 7 | 8 | bool isPropertyCompatible(Property value); 9 | } 10 | -------------------------------------------------------------------------------- /patterns/state/three_state/main.dart: -------------------------------------------------------------------------------- 1 | import 'pattern/switcher.dart'; 2 | import 'states/one.dart'; 3 | 4 | void main() { 5 | final switcher = Switcher( 6 | initState: One(), 7 | ); 8 | 9 | switcher.call(); // call(1): One 10 | switcher.call(); // call(2): Two 11 | switcher.call(); // call(3): Three 12 | switcher.call(); // call:(4) One 13 | } 14 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/pattern/context.dart: -------------------------------------------------------------------------------- 1 | import '../variable/bool_variable.dart'; 2 | 3 | class Context { 4 | void assign(BoolVariable variable, bool value) { 5 | _values.putIfAbsent(variable.name, () => value); 6 | } 7 | 8 | bool lookup(String name) { 9 | return _values[name]!; 10 | } 11 | 12 | final _values = {}; 13 | } 14 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/app/app.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/manipulator.dart'; 2 | import 'shapes.dart'; 3 | import 'tool.dart'; 4 | 5 | class App { 6 | final Shapes shapes; 7 | final Manipulator manipulator; 8 | final List tools; 9 | 10 | App({ 11 | required this.shapes, 12 | required this.manipulator, 13 | required this.tools, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /patterns/iterator/word_iterator/main.dart: -------------------------------------------------------------------------------- 1 | import 'text/text.dart'; 2 | 3 | void main() { 4 | final text = Text( 5 | 'Iterator is a behavioral design pattern that lets you traverse elements ' 6 | 'of a collection without exposing its underlying representation ' 7 | '(list, stack, tree, etc.).', 8 | ); 9 | 10 | for (final s in text) { 11 | print(s); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/formats/json_format.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'text_format.dart'; 3 | 4 | class JsonFormat extends TextFormat { 5 | final _list = >[]; 6 | 7 | void add(Map item) { 8 | _list.add(item); 9 | } 10 | 11 | @override 12 | String get content => JsonEncoder.withIndent(' ').convert(_list); 13 | } 14 | -------------------------------------------------------------------------------- /patterns/composite/image_editor/shapes/shape.dart: -------------------------------------------------------------------------------- 1 | import '../editor/image_editor.dart'; 2 | 3 | abstract class Shape { 4 | int get x; 5 | 6 | int get y; 7 | 8 | int get width; 9 | 10 | int get height; 11 | 12 | void move(int x, int y); 13 | 14 | void select(); 15 | 16 | void unSelect(); 17 | 18 | bool get isSelected; 19 | 20 | void paint(Graphics graphics); 21 | } 22 | -------------------------------------------------------------------------------- /patterns/bridge/clock/clocks/clock.dart: -------------------------------------------------------------------------------- 1 | import '../bells/bell.dart'; 2 | 3 | abstract class Clock { 4 | final int seconds; 5 | final Bell bell; 6 | 7 | Clock({ 8 | required this.seconds, 9 | required this.bell, 10 | }); 11 | 12 | void start(); 13 | 14 | void showBell(String message) { 15 | bell.notify('$message(sec: $seconds): '); 16 | bell.ring(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /patterns/strategy/view_strategy/pattern/byte_context.dart: -------------------------------------------------------------------------------- 1 | import 'view_strategy.dart'; 2 | 3 | class ByteContext { 4 | String toStringView(ViewStrategy strategy) { 5 | return '${strategy.runtimeType}:\n' 6 | '${strategy.out(this)}'; 7 | } 8 | 9 | void add(dynamic value) { 10 | _buf.add(value); 11 | } 12 | 13 | List toList() => _buf; 14 | 15 | final _buf = []; 16 | } 17 | -------------------------------------------------------------------------------- /patterns/bridge/clock/clocks/once.dart: -------------------------------------------------------------------------------- 1 | import '../bells/bell.dart'; 2 | import 'clock.dart'; 3 | 4 | class Once extends Clock { 5 | Once({required int seconds, required Bell bell}) 6 | : super(seconds: seconds, bell: bell); 7 | 8 | @override 9 | void start() { 10 | Future.delayed(Duration(seconds: seconds), () { 11 | showBell('Once'); 12 | print(''); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/buttons/html_button.dart: -------------------------------------------------------------------------------- 1 | import 'button.dart'; 2 | 3 | /// EN: HTML button implementation. 4 | /// 5 | /// RU: Реализация HTML кнопок. 6 | class HtmlButton extends Button { 7 | HtmlButton(void Function() onClick) : super(onClick); 8 | 9 | @override 10 | void render() { 11 | print(''); 12 | onClick(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/dialogs_factory/html_dialog.dart: -------------------------------------------------------------------------------- 1 | import '../buttons/button.dart'; 2 | import '../buttons/html_button.dart'; 3 | import 'dialog.dart'; 4 | 5 | /// EN: HTML Dialog will produce HTML buttons. 6 | /// 7 | /// RU: HTML-диалог. 8 | class HtmlDialog extends Dialog { 9 | @override 10 | Button createButton(void Function() onClick) => HtmlButton(onClick); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/mediator/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | import 'components/component1.dart'; 2 | import 'components/component2.dart'; 3 | import 'concrete_mediator/concrete_mediator.dart'; 4 | 5 | void main() { 6 | final component1 = Component1(); 7 | final component2 = Component2(); 8 | 9 | ConcreteMediator(component1, component2); 10 | 11 | component1.doOne(); 12 | print(''); 13 | component2.doTwo(); 14 | } 15 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | import 'concrete_builder/concrete_builder_1.dart'; 2 | import 'concrete_builder/concrete_builder_2.dart'; 3 | import 'pattern/director.dart'; 4 | 5 | void main() { 6 | final director = Director(); 7 | 8 | final product1 = director.construct(ConcreteBuilder1()); 9 | print(product1); 10 | 11 | final product2 = director.construct(ConcreteBuilder2()); 12 | print(product2); 13 | } 14 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/main.dart: -------------------------------------------------------------------------------- 1 | import 'miners/csv_miner.dart'; 2 | import 'miners/zip_miner.dart'; 3 | import 'miners/html_analyzer.dart'; 4 | 5 | void main() { 6 | print('HTMLMiner'); 7 | HTMLMiner('google_workers.html').mine(); 8 | 9 | print('CsvMiner'); 10 | CsvMiner('twitter_workers.csv').mine(); 11 | 12 | print('GuruMiner'); 13 | ZipMiner('refactoring_guru_workers.zip').mine(); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /patterns/proxy/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | import 'pattern/proxy.dart'; 2 | import 'pattern/subject.dart'; 3 | 4 | void main() async { 5 | final subject = Proxy(); 6 | client(subject); // print "Proxy data" 7 | 8 | print('Wait 2 seconds...'); 9 | await Future.delayed(Duration(seconds: 2)); 10 | 11 | client(subject); // print "Real data" 12 | } 13 | 14 | void client(Subject subject) { 15 | print(subject.request()); 16 | } 17 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/utils/formatted_table.dart: -------------------------------------------------------------------------------- 1 | class FormattedTable { 2 | void addRow(a, b) { 3 | _buff.writeln( 4 | '${' ' * 1}${a.padRight(20)}${b.toString().padLeft(7)}', 5 | ); 6 | } 7 | 8 | void addLine() { 9 | _buff.writeln('-' * 31); 10 | } 11 | 12 | @override 13 | String toString() { 14 | return _buff.toString(); 15 | } 16 | 17 | final _buff = StringBuffer(); 18 | } 19 | -------------------------------------------------------------------------------- /patterns/visitor/shapes_exporter/visitor/visitor.dart: -------------------------------------------------------------------------------- 1 | import '../shapes/circle.dart'; 2 | import '../shapes/compound_shape.dart'; 3 | import '../shapes/dot.dart'; 4 | import '../shapes/rectangle.dart'; 5 | 6 | abstract class Visitor { 7 | void visitCompoundShape(CompoundShape compound); 8 | 9 | void visitDot(Dot dot); 10 | 11 | void visitCircle(Circle circle); 12 | 13 | void visitRectangle(Rectangle rectangle); 14 | } 15 | -------------------------------------------------------------------------------- /patterns/decorator/data_source_decoder/src/data_source_decorator.dart: -------------------------------------------------------------------------------- 1 | import 'data_source.dart'; 2 | 3 | class DataSourceDecorator implements DataSource { 4 | final DataSource _wrapper; 5 | 6 | DataSourceDecorator(this._wrapper); 7 | 8 | @override 9 | void writeData(String data) { 10 | _wrapper.writeData(data); 11 | } 12 | 13 | @override 14 | String readData() { 15 | return _wrapper.readData(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/buttons/windows_button.dart: -------------------------------------------------------------------------------- 1 | import 'button.dart'; 2 | 3 | /// EN: Windows button implementation. 4 | /// 5 | /// RU: Реализация нативных кнопок операционной системы. 6 | class WindowsButton extends Button { 7 | WindowsButton(void Function() onClick) : super(onClick); 8 | 9 | @override 10 | void render() { 11 | print('Windows Button'); 12 | onClick(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /patterns/visitor/shapes_exporter/shapes/dot.dart: -------------------------------------------------------------------------------- 1 | import '../visitor/visitor.dart'; 2 | import 'shape.dart'; 3 | 4 | class Dot implements Shape { 5 | final int x; 6 | final int y; 7 | 8 | Dot({ 9 | required this.x, 10 | required this.y, 11 | }); 12 | 13 | @override 14 | void accept(Visitor visitor) { 15 | visitor.visitDot(this); 16 | } 17 | 18 | @override 19 | void draw() { 20 | // ... 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/some_complex_media_library/bitrate_reader.dart: -------------------------------------------------------------------------------- 1 | import 'codec.dart'; 2 | import 'video_file.dart'; 3 | 4 | class BitrateReader { 5 | static VideoFile read(VideoFile file, Codec codec) { 6 | print('BitrateReader: reading file...'); 7 | return file; 8 | } 9 | 10 | static VideoFile convert(VideoFile buffer, Codec codec) { 11 | print('BitrateReader: writing file...'); 12 | return buffer; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /patterns/builder/cars/README.md: -------------------------------------------------------------------------------- 1 | # Builder pattern 2 | 3 | **Description:** 4 | https://refactoring.guru/design-patterns/builder?#pseudocode 5 | 6 | **Output:** 7 | 8 | ``` 9 | Car built: 10 | CarType.sportCar 11 | 12 | Car manual built: 13 | Type of car: CarType.sportCar 14 | Count of seats: 2 15 | Engine: volume - 3.0; mileage - 0.0 16 | Transmission: Transmission.semiAutomatic 17 | Trip Computer: Functional 18 | GPS Navigator: Functional 19 | ``` 20 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/dialogs_factory/windows_dialog.dart: -------------------------------------------------------------------------------- 1 | import '../buttons/button.dart'; 2 | import '../buttons/windows_button.dart'; 3 | import 'dialog.dart'; 4 | 5 | /// EN: Windows Dialog will produce Windows buttons. 6 | /// 7 | /// RU: Диалог на элементах операционной системы. 8 | class WindowsDialog extends Dialog { 9 | @override 10 | Button createButton(void Function() onClick) => WindowsButton(onClick); 11 | } 12 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/creations/text_creation_state.dart: -------------------------------------------------------------------------------- 1 | import '../../shapes/shape.dart'; 2 | import '../../shapes/text_shape.dart'; 3 | import '../_/creation_state.dart'; 4 | 5 | class TextCreationState extends CreationState { 6 | @override 7 | Shape createShape(double x, double y) { 8 | return TextShape(x, y, 2); 9 | } 10 | 11 | @override 12 | String toString() { 13 | return 'Text Creation State'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/factories/mac_os_factory.dart: -------------------------------------------------------------------------------- 1 | import '../button/button.dart'; 2 | import '../checkbox/checkbox.dart'; 3 | import '../button/mac_os_button.dart'; 4 | import '../checkbox/mac_os_checkbox.dart'; 5 | import 'gui_factory.dart'; 6 | 7 | class MacOSFactory implements GUIFactory { 8 | @override 9 | Button createButton() => MacOSButton(); 10 | 11 | @override 12 | Checkbox createCheckbox() => MacOSCheckbox(); 13 | } 14 | -------------------------------------------------------------------------------- /patterns/builder/cars/director/gps_navigation.dart: -------------------------------------------------------------------------------- 1 | /// EN: Just another feature of a car. 2 | /// 3 | /// RU: Одна из фишек автомобиля. 4 | class GPSNavigator { 5 | late String _route; 6 | 7 | GPSNavigator() 8 | : _route = "221b, Baker Street, London " 9 | "to Scotland Yard, 8-10 Broadway, London"; 10 | 11 | GPSNavigator.fromRout(String manualRoute) { 12 | _route = manualRoute; 13 | } 14 | 15 | String get root => _route; 16 | } 17 | -------------------------------------------------------------------------------- /patterns/chain_of_responsibility/server_middleware/server/middleware.dart: -------------------------------------------------------------------------------- 1 | part of server; 2 | 3 | abstract class Middleware { 4 | final Middleware? next; 5 | 6 | Middleware({ 7 | this.next, 8 | }); 9 | 10 | bool check(String email, String password); 11 | 12 | bool checkNext(String email, String password) { 13 | return next?.check(email, password) ?? true; 14 | } 15 | 16 | Server? _server; 17 | 18 | Server? get server => _server; 19 | } 20 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/factories/window_factory.dart: -------------------------------------------------------------------------------- 1 | import '../button/button.dart'; 2 | import '../checkbox/checkbox.dart'; 3 | import '../button/windows_button.dart'; 4 | import '../checkbox/windows_checkbox.dart'; 5 | import 'gui_factory.dart'; 6 | 7 | class WindowsFactory implements GUIFactory { 8 | @override 9 | Button createButton() => WindowsButton(); 10 | 11 | @override 12 | Checkbox createCheckbox() => WindowsCheckbox(); 13 | } 14 | -------------------------------------------------------------------------------- /patterns/iterator/github_commit/pattern/github_repo.dart: -------------------------------------------------------------------------------- 1 | import '../github/commit.dart'; 2 | 3 | class GitHubRepo { 4 | GitHubRepo(this._json); 5 | 6 | Iterable commitIterator() sync* { 7 | for (final jsonCommit in _json) { 8 | var message = jsonCommit['commit']['message'] as String; 9 | message = message.replaceAll(RegExp(r'\n+'), ' '); 10 | 11 | yield Commit(message); 12 | } 13 | } 14 | 15 | final List _json; 16 | } 17 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/creations/circle_creation_state.dart: -------------------------------------------------------------------------------- 1 | import '../../shapes/circle_shape.dart'; 2 | import '../../shapes/shape.dart'; 3 | import '../_/creation_state.dart'; 4 | 5 | class CircleCreationState extends CreationState { 6 | @override 7 | Shape createShape(double x, double y) { 8 | return CircleShape(x, y, 100, 100, 25); 9 | } 10 | 11 | @override 12 | String toString() { 13 | return 'Circle Creation State'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /patterns/strategy/reservation_cargo_spaces/policy/overbooking_policy.dart: -------------------------------------------------------------------------------- 1 | import '../partners/cargo.dart'; 2 | import '../partners/voyage.dart'; 3 | 4 | class OverbookingPolicy { 5 | static const allowableRedundancy = 1.1; 6 | 7 | bool isAllowed(Cargo cargo, Voyage voyage) { 8 | final maxBooking = voyage.capacity * allowableRedundancy; 9 | final futureWeight = voyage.bookedCargoSize() + cargo.size; 10 | 11 | return futureWeight < maxBooking; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/creations/rectangle_creation_state.dart: -------------------------------------------------------------------------------- 1 | import '../../shapes/rectangle_shape.dart'; 2 | import '../../shapes/shape.dart'; 3 | import '../_/creation_state.dart'; 4 | 5 | class RectangleCreationState extends CreationState { 6 | @override 7 | Shape createShape(double x, double y) { 8 | return RectangleShape(x, y, 0, 0); 9 | } 10 | 11 | @override 12 | String toString() { 13 | return 'Rectangle Creation State'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/shapes/shape.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../states/selections/selection_state.dart'; 4 | 5 | abstract class Shape { 6 | double get x; 7 | 8 | double get y; 9 | 10 | double get width; 11 | 12 | double get height; 13 | 14 | Rect get rect; 15 | 16 | void move(double x, double y); 17 | 18 | void resize(double width, double height); 19 | 20 | void paint(Canvas canvas); 21 | 22 | SelectionState createSelectionState(); 23 | } 24 | -------------------------------------------------------------------------------- /patterns/strategy/reservation_cargo_spaces/partners/voyage.dart: -------------------------------------------------------------------------------- 1 | import 'cargo.dart'; 2 | 3 | class Voyage { 4 | final _cargo = {}; 5 | 6 | double get capacity => 2000.0; 7 | 8 | double bookedCargoSize() { 9 | return _cargo.values.fold(0, (prev, cargo) => prev + cargo.size); 10 | } 11 | 12 | void addCargo(Cargo cargo, int confirmation) { 13 | _cargo.putIfAbsent(confirmation, () => cargo); 14 | print('Add Cargo(${cargo.size}) to voyage.'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /patterns/command/conceptual/mut_str/mut_str.dart: -------------------------------------------------------------------------------- 1 | class MutStr { 2 | void push(String str) { 3 | _buff.addAll(str.split('')); 4 | } 5 | 6 | void insert(int pos, String str) { 7 | _buff.insert(pos, str); 8 | } 9 | 10 | void delete(int startPos, int len) { 11 | _buff.removeRange(startPos, len); 12 | } 13 | 14 | int get len => _buff.length; 15 | 16 | @override 17 | String toString() { 18 | return _buff.join(''); 19 | } 20 | 21 | final _buff = []; 22 | } 23 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/memento_pattern/caretaker.dart: -------------------------------------------------------------------------------- 1 | import 'memento.dart'; 2 | import 'snapshot.dart'; 3 | 4 | class Caretaker { 5 | final _mementoList = []; 6 | 7 | List get list => List.unmodifiable(_mementoList); 8 | 9 | void addMemento(Memento memento) { 10 | _mementoList.add(memento); 11 | } 12 | 13 | bool isSnapshotExists(Snapshot snapshot) { 14 | return list.any( 15 | (e) => e.snapshot == snapshot, 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /patterns/state/three_state/pattern/switcher.dart: -------------------------------------------------------------------------------- 1 | library switcher; 2 | 3 | part 'state.dart'; 4 | 5 | class Switcher { 6 | Switcher({required State initState}) { 7 | changeState(initState); 8 | } 9 | 10 | int get calls => _calls; 11 | 12 | void call() { 13 | _calls++; 14 | _state.call(); 15 | } 16 | 17 | void changeState(State newState) { 18 | _state = newState; 19 | _state._context = this; 20 | } 21 | 22 | late State _state; 23 | int _calls = 0; 24 | } 25 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/converters/console_converter.dart: -------------------------------------------------------------------------------- 1 | import '../formats/console_format.dart'; 2 | import 'converter.dart'; 3 | 4 | class ConsoleConverter extends Converter { 5 | @override 6 | final result = ConsoleFormat(); 7 | 8 | @override 9 | void writeColor(String color) { 10 | result.color = color; 11 | result.write(color); 12 | result.color = 'black'; 13 | } 14 | 15 | @override 16 | void writeWord(String text) => result.write(text); 17 | } 18 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/product/concrete_product_2.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/product.dart'; 2 | 3 | class ConcreteProduct2 implements Product { 4 | ConcreteProduct2(String name) { 5 | _buff.add(name); 6 | } 7 | 8 | void addLine(String name) { 9 | final num = ['1️⃣', '2️⃣', '3️⃣'][_buff.length - 1]; 10 | _buff.add('$num: $name'); 11 | } 12 | 13 | @override 14 | String toString() { 15 | return '${_buff.join('\n')}\n'; 16 | } 17 | 18 | final _buff = []; 19 | } 20 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/app/application.dart: -------------------------------------------------------------------------------- 1 | import '../button/button.dart'; 2 | import '../checkbox/checkbox.dart'; 3 | import '../factories/gui_factory.dart'; 4 | 5 | class Application { 6 | late Button _button; 7 | late Checkbox _checkbox; 8 | 9 | Application(GUIFactory factory) { 10 | _button = factory.createButton(); 11 | _checkbox = factory.createCheckbox(); 12 | } 13 | 14 | void paint() { 15 | _button.paint(); 16 | _checkbox.paint(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/product/concrete_product_1.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/product.dart'; 2 | 3 | class ConcreteProduct1 implements Product { 4 | ConcreteProduct1(String name) { 5 | _buff.add(name); 6 | } 7 | 8 | void addLine(String name) { 9 | final index = _buff.length.toString().padLeft(3, '0'); 10 | _buff.add('$index: $name'); 11 | } 12 | 13 | @override 14 | String toString() { 15 | return '${_buff.join('\n')}\n'; 16 | } 17 | 18 | final _buff = []; 19 | } 20 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/products/product_leaf.dart: -------------------------------------------------------------------------------- 1 | import '../diagram/diagram.dart'; 2 | import 'product.dart'; 3 | 4 | class ProductLeaf implements Product { 5 | ProductLeaf(this.name, this.price); 6 | 7 | @override 8 | String get content => '$name($price\$)'; 9 | 10 | final String name; 11 | 12 | @override 13 | final int price; 14 | 15 | @override 16 | int get size => 1; 17 | 18 | @override 19 | Diagram toDiagram() { 20 | return Diagram.node(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /patterns/flyweight/conceptual/pattern/flyweight.dart: -------------------------------------------------------------------------------- 1 | import '../params/share_params.dart'; 2 | 3 | class Flyweight { 4 | final String localParam; 5 | final ShareParams shareParams; 6 | 7 | Flyweight(this.localParam, this.shareParams); 8 | 9 | void draw() { 10 | print('Flyweight('); 11 | print('\tlocalParam: "$localParam", shareParam: (' 12 | '${shareParams.param1}, ' 13 | '"${shareParams.param2}", ' 14 | '${shareParams.param3})'); 15 | print(')'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/pattern/manipulation_state.dart: -------------------------------------------------------------------------------- 1 | part of manipulator; 2 | 3 | class ManipulationState { 4 | Manipulator get context => _context; 5 | 6 | void init() {} 7 | 8 | void mouseMove(double x, double y) {} 9 | 10 | void mouseDown(double x, double y) {} 11 | 12 | void mouseUp() {} 13 | 14 | void mouseDoubleClick(double x, double y) {} 15 | 16 | void keyDown(KeyEvent keyEvent) {} 17 | 18 | void paint(Canvas canvas) {} 19 | 20 | late Manipulator _context; 21 | } 22 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/variable/bool_variable.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/context.dart'; 2 | import '../pattern/expression.dart'; 3 | 4 | class BoolVariable implements Expression { 5 | BoolVariable(this.name); 6 | 7 | final String name; 8 | 9 | @override 10 | bool evaluate(Context context) { 11 | return context.lookup(name); 12 | } 13 | 14 | @override 15 | String toDebugString(Context context) { 16 | final value = context.lookup(name); 17 | return '$name($value)'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /patterns/composite/image_editor/shapes/dot.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | import '../editor/image_editor.dart'; 4 | import 'base_shape.dart'; 5 | 6 | class Dot extends BaseShape { 7 | Dot(int x, int y, Color color) : super(x, y, color); 8 | 9 | @override 10 | int get width => 1; 11 | 12 | @override 13 | int get height => 1; 14 | 15 | @override 16 | void paint(Graphics graphics) { 17 | super.paint(graphics); 18 | graphics.setPixel(x, y); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /patterns/memento/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | import 'app/command.dart'; 2 | import 'app/editor.dart'; 3 | 4 | void main() { 5 | final editor = Editor('New Document'); 6 | final firstState = Command.makeBackup(editor); 7 | editor.text += ' add text'; 8 | final secondState = Command.makeBackup(editor); 9 | 10 | print('Current state: "${editor.text}"'); 11 | 12 | firstState.undo(); 13 | print('First state: "${editor.text}"'); 14 | 15 | secondState.undo(); 16 | print('Second state: "${editor.text}"'); 17 | } 18 | -------------------------------------------------------------------------------- /patterns/observer/open_close_editor_events/listeners/email_notification_listener.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'event_listener.dart'; 4 | 5 | class EmailNotificationListener implements EventListener { 6 | String email; 7 | 8 | EmailNotificationListener(this.email); 9 | 10 | @override 11 | void update(String eventType, File file) { 12 | print('Email to "$email": ' 13 | 'Someone has performed "$eventType" ' 14 | 'operation with the following file: "${file.path}"'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /patterns/command/text_editor/application/text_cursor.dart: -------------------------------------------------------------------------------- 1 | class TextCursor { 2 | int get position => _position; 3 | 4 | int get startSelection => _startSelection ?? _position; 5 | 6 | int get endSelection => position; 7 | 8 | bool get isTextSelected => _startSelection != null; 9 | 10 | final int _position; 11 | 12 | final int? _startSelection; 13 | 14 | TextCursor.fromPosition(this._position) : _startSelection = null; 15 | 16 | TextCursor.fromSelection(this._startSelection, this._position); 17 | } 18 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/factories/gui_factory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../button/button.dart'; 4 | import '../checkbox/checkbox.dart'; 5 | import 'mac_os_factory.dart'; 6 | import 'window_factory.dart'; 7 | 8 | abstract class GUIFactory { 9 | factory GUIFactory() { 10 | if (Platform.isMacOS) { 11 | return MacOSFactory(); 12 | } else { 13 | return WindowsFactory(); 14 | } 15 | } 16 | 17 | Button createButton(); 18 | 19 | Checkbox createCheckbox(); 20 | } 21 | -------------------------------------------------------------------------------- /patterns/visitor/shapes_exporter/shapes/circle.dart: -------------------------------------------------------------------------------- 1 | import '../visitor/visitor.dart'; 2 | import 'shape.dart'; 3 | 4 | class Circle implements Shape { 5 | final int xCenter; 6 | final int yCenter; 7 | final int radius; 8 | 9 | Circle({ 10 | required this.xCenter, 11 | required this.yCenter, 12 | required this.radius, 13 | }); 14 | 15 | @override 16 | void accept(Visitor visitor) { 17 | visitor.visitCircle(this); 18 | } 19 | 20 | @override 21 | void draw() { 22 | // ... 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patterns/builder/cars/director/trip_computer.dart: -------------------------------------------------------------------------------- 1 | import '../cars/car.dart'; 2 | 3 | /// EN: Just another feature of a car. 4 | /// 5 | /// RU: Одна из фишек автомобиля. 6 | class TripComputer { 7 | Car? car; 8 | 9 | void showFuelLevel() { 10 | print("Fuel level: ${car?.fuel}"); 11 | } 12 | 13 | void showStatus() { 14 | final engine = car?.engine; 15 | if (engine != null && engine.isStarted) { 16 | print("Car is started"); 17 | } else { 18 | print("Car isn't started"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /patterns/flyweight/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names 2 | 3 | import 'pattern/flyweight_factory.dart'; 4 | 5 | void main() { 6 | final factory = FlyweightFactory(); 7 | 8 | final one = factory.create('one'); 9 | one.draw(); 10 | 11 | final ONE = factory.create('ONE'); 12 | ONE.draw(); 13 | 14 | final OnE = factory.create('OnE'); 15 | OnE.draw(); 16 | 17 | final two = factory.create('two'); 18 | two.draw(); 19 | 20 | final Two = factory.create('Two'); 21 | Two.draw(); 22 | } 23 | -------------------------------------------------------------------------------- /patterns/observer/open_close_editor_events/listeners/log_open_listener.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'event_listener.dart'; 4 | 5 | class LogOpenListener implements EventListener { 6 | File logFile; 7 | 8 | LogOpenListener(String logFileName) : logFile = File(logFileName); 9 | 10 | @override 11 | void update(String eventType, File file) { 12 | print('Save to log "${logFile.path}": ' 13 | 'Someone has performed "$eventType" ' 14 | 'operation with the following file: "${file.path}"'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /patterns/iterator/github_commit/main.dart: -------------------------------------------------------------------------------- 1 | import 'github/commit.dart'; 2 | import 'github/github_loader.dart'; 3 | import 'pattern/github_repo.dart'; 4 | 5 | void main() async { 6 | final GitHubRepo gitHubRepo = await GitHubLoader.get( 7 | userName: 'RefactoringGuru', 8 | repoName: 'design-patterns-dart', 9 | ); 10 | 11 | print( 12 | 'Iterate last 10 commits.' 13 | '\n----------------------------', 14 | ); 15 | 16 | for (Commit commit in gitHubRepo.commitIterator()) { 17 | print(commit.message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/utils/analysis.dart: -------------------------------------------------------------------------------- 1 | import 'raw_data.dart'; 2 | 3 | class Analysis { 4 | Analysis(StringTable table) { 5 | _analyze(table); 6 | } 7 | 8 | void _analyze(StringTable table) { 9 | int len = 0; 10 | double sum = 0.0; 11 | 12 | for (final row in table) { 13 | final index = double.parse(row[1]); 14 | sum += index; 15 | len++; 16 | } 17 | 18 | this.len = len; 19 | this.sum = sum; 20 | } 21 | 22 | late final int len; 23 | late final double sum; 24 | } 25 | -------------------------------------------------------------------------------- /patterns/observer/open_close_editor_events/editor/editor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../event_manager/event_manager.dart'; 4 | 5 | class Editor { 6 | final events = EventManager(['open', 'save']); 7 | 8 | File? _file; 9 | 10 | void openFile(String filePath) { 11 | _file = File(filePath); 12 | events.notify("open", _file!); 13 | } 14 | 15 | void saveFile() { 16 | if (_file == null) { 17 | throw Exception('Please open a file first.'); 18 | } 19 | 20 | events.notify('save', _file!); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /patterns/singleton/conceptual/pattern/singleton.dart: -------------------------------------------------------------------------------- 1 | abstract class Singleton { 2 | factory Singleton() { 3 | if (_instance == null) { 4 | print('Create singleton once.'); 5 | _instance = ConcreteSingleton(); 6 | } 7 | 8 | return _instance!; 9 | } 10 | 11 | static Singleton get instance => Singleton(); 12 | 13 | void doSome(); 14 | 15 | static Singleton? _instance; 16 | } 17 | 18 | class ConcreteSingleton implements Singleton { 19 | @override 20 | void doSome() { 21 | print('doSome()'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /patterns/visitor/shapes_exporter/shapes/compound_shape.dart: -------------------------------------------------------------------------------- 1 | import '../visitor/visitor.dart'; 2 | import 'shape.dart'; 3 | 4 | class CompoundShape implements Shape { 5 | final int x; 6 | final int y; 7 | final List children; 8 | 9 | CompoundShape({ 10 | required this.x, 11 | required this.y, 12 | required this.children, 13 | }); 14 | 15 | @override 16 | void accept(Visitor visitor) { 17 | visitor.visitCompoundShape(this); 18 | } 19 | 20 | @override 21 | void draw() { 22 | // ... 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/client_app/business_rules/color_rules.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | class ColorRules { 4 | final colors = [ 5 | Color(0xFF000000), 6 | Color(0xFFD81B60), 7 | Color(0xFF5E35B1), 8 | Color(0xFF1E88E5), 9 | Color(0xFF43A047), 10 | ]; 11 | 12 | Color nextColor(Color currentColor) { 13 | var nextIndex = colors.indexOf(currentColor) + 1; 14 | 15 | if (nextIndex >= colors.length || nextIndex < 0) { 16 | nextIndex = 0; 17 | } 18 | 19 | return colors[nextIndex]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /patterns/observer/subscriber_flutter_widget/widgets/hash_generator_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HashGeneratorWidget extends StatelessWidget { 4 | final void Function() onHashGenerate; 5 | 6 | const HashGeneratorWidget({ 7 | Key? key, 8 | required this.onHashGenerate, 9 | }) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return ElevatedButton( 14 | child: Text('Generate new hash'), 15 | onPressed: onHashGenerate, 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /patterns/proxy/conceptual/pattern/proxy.dart: -------------------------------------------------------------------------------- 1 | import 'subject.dart'; 2 | import 'real_subject.dart'; 3 | 4 | class Proxy implements Subject { 5 | @override 6 | String request() { 7 | if (isSubjectLoaded) { 8 | return _subject!.request(); 9 | } 10 | 11 | _load(); 12 | return 'Proxy data.'; 13 | } 14 | 15 | bool get isSubjectLoaded => _subject != null; 16 | 17 | void _load() async { 18 | Future.delayed(Duration(seconds: 1), () { 19 | _subject = RealSubject(); 20 | }); 21 | } 22 | 23 | Subject? _subject; 24 | } 25 | -------------------------------------------------------------------------------- /patterns/visitor/shapes_exporter/shapes/rectangle.dart: -------------------------------------------------------------------------------- 1 | import '../visitor/visitor.dart'; 2 | import 'shape.dart'; 3 | 4 | class Rectangle implements Shape { 5 | final int x; 6 | final int y; 7 | final int width; 8 | final int height; 9 | 10 | Rectangle({ 11 | required this.x, 12 | required this.y, 13 | required this.width, 14 | required this.height, 15 | }); 16 | 17 | @override 18 | void accept(Visitor visitor) { 19 | visitor.visitRectangle(this); 20 | } 21 | 22 | @override 23 | void draw() { 24 | // ... 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /patterns/bridge/clock/clocks/interval.dart: -------------------------------------------------------------------------------- 1 | import '../bells/bell.dart'; 2 | import 'clock.dart'; 3 | 4 | class Interval extends Clock { 5 | Interval({required int seconds, required Bell bell}) 6 | : super(bell: bell, seconds: seconds); 7 | 8 | @override 9 | void start() { 10 | Future.delayed(Duration(seconds: seconds), () async { 11 | showBell('Interval'); 12 | for (var i = 0; i < 3 - 1; i++) { 13 | await Future.delayed(Duration(milliseconds: 500)); 14 | bell.ring(); 15 | } 16 | print(''); 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /patterns/flyweight/conceptual/pattern/flyweight_factory.dart: -------------------------------------------------------------------------------- 1 | import 'flyweight.dart'; 2 | import '../params/share_params.dart'; 3 | import '../utils/fake_value.dart'; 4 | 5 | class FlyweightFactory { 6 | Flyweight create(String localParam) { 7 | final shareParams = _shares.putIfAbsent( 8 | localParam.toLowerCase(), 9 | () => ShareParams( 10 | fakeInt, 11 | fakeString, 12 | fakeDouble, 13 | ), 14 | ); 15 | 16 | return Flyweight(localParam, shareParams); 17 | } 18 | 19 | final _shares = {}; 20 | } 21 | -------------------------------------------------------------------------------- /patterns/chain_of_responsibility/server_middleware/middleware/role_check_middleware.dart: -------------------------------------------------------------------------------- 1 | import '../server/server.dart'; 2 | 3 | class RoleCheckMiddleware extends Middleware { 4 | RoleCheckMiddleware({Middleware? next}) : super(next: next); 5 | 6 | @override 7 | bool check(String email, String password) { 8 | if (email == 'admin@example.com') { 9 | print('RoleCheckMiddleware: role defined as "admin"'); 10 | return true; 11 | } 12 | 13 | print('RoleCheckMiddleware: role defined as "user"'); 14 | return checkNext(email, password); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /patterns/strategy/reservation_cargo_spaces/main.dart: -------------------------------------------------------------------------------- 1 | import 'application/application.dart'; 2 | import 'partners/cargo.dart'; 3 | import 'partners/voyage.dart'; 4 | import 'policy/overbooking_policy.dart'; 5 | 6 | void main() { 7 | final overbookingPolicy = OverbookingPolicy(); 8 | final app = Application(overbookingPolicy); 9 | final voyage = Voyage(); 10 | 11 | try { 12 | app.makeBooking(Cargo(1000), voyage); 13 | app.makeBooking(Cargo(500), voyage); 14 | app.makeBooking(Cargo(800), voyage); // error 15 | } catch (e) { 16 | print(e); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/app/app.dart: -------------------------------------------------------------------------------- 1 | import '../app/tools.dart'; 2 | import 'shapes.dart'; 3 | 4 | class App { 5 | final Tools tools; 6 | final Shapes shapes; 7 | 8 | App({ 9 | required this.tools, 10 | required this.shapes, 11 | }); 12 | 13 | void addShape(double x, double y) { 14 | final activeColor = tools.activeColor.value; 15 | final activeFactory = tools.activeFactory.value; 16 | 17 | final newShape = activeFactory.createShape(x, y, activeColor); 18 | newShape.centerToFit(); 19 | shapes.add(newShape); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /patterns/prototype/shapes/shape/rectangle.dart: -------------------------------------------------------------------------------- 1 | import 'shape.dart'; 2 | 3 | class Rectangle extends Shape { 4 | final int width; 5 | final int height; 6 | final String color; 7 | 8 | Rectangle({ 9 | required int x, 10 | required int y, 11 | required this.width, 12 | required this.height, 13 | required this.color, 14 | }) : super(x: x, y: y); 15 | 16 | @override 17 | Rectangle clone() { 18 | return Rectangle( 19 | x: x, 20 | y: y, 21 | width: width, 22 | height: height, 23 | color: color, 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/some_complex_media_library/codec_factory.dart: -------------------------------------------------------------------------------- 1 | import 'codec.dart'; 2 | import 'mpeg4_compression_codec.dart'; 3 | import 'ogg_compression_codec.dart'; 4 | import 'video_file.dart'; 5 | 6 | class CodecFactory { 7 | static Codec extract(VideoFile file) { 8 | String type = file.codecType; 9 | if (type == 'mp4') { 10 | print('CodecFactory: extracting mpeg audio...'); 11 | return MPEG4CompressionCodec(); 12 | } else { 13 | print('CodecFactory: extracting ogg audio...'); 14 | return OggCompressionCodec(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /patterns/observer/open_close_editor_events/main.dart: -------------------------------------------------------------------------------- 1 | import 'editor/editor.dart'; 2 | import 'listeners/email_notification_listener.dart'; 3 | import 'listeners/log_open_listener.dart'; 4 | 5 | void main() { 6 | final editor = Editor(); 7 | editor.events 8 | ..subscribe( 9 | 'open', 10 | LogOpenListener('log.txt'), 11 | ) 12 | ..subscribe( 13 | 'save', 14 | EmailNotificationListener('admin@example.com'), 15 | ); 16 | 17 | try { 18 | editor.openFile('test.txt'); 19 | editor.saveFile(); 20 | } catch (e) { 21 | print(e); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /patterns/bridge/clock/main.dart: -------------------------------------------------------------------------------- 1 | import 'bells/melody.dart'; 2 | import 'bells/signal.dart'; 3 | import 'clocks/once.dart'; 4 | import 'clocks/clock.dart'; 5 | import 'clocks/interval.dart'; 6 | 7 | void main() { 8 | final melody = Melody(); 9 | final signal = Signal(); 10 | startClocks([ 11 | Once(seconds: 1, bell: melody), 12 | Once(seconds: 2, bell: signal), 13 | Interval(seconds: 5, bell: melody), 14 | Interval(seconds: 3, bell: signal), 15 | ]); 16 | } 17 | 18 | void startClocks(List clocks) { 19 | for (final clock in clocks) { 20 | clock.start(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /patterns/prototype/shapes/shape/circle.dart: -------------------------------------------------------------------------------- 1 | import 'rectangle.dart'; 2 | 3 | class Circle extends Rectangle { 4 | final int radius; 5 | 6 | Circle({ 7 | required int x, 8 | required int y, 9 | required this.radius, 10 | required String color, 11 | }) : super( 12 | x: x, 13 | y: y, 14 | width: radius, 15 | height: radius, 16 | color: color, 17 | ); 18 | 19 | @override 20 | Circle clone() { 21 | return Circle( 22 | x: x, 23 | y: y, 24 | radius: radius, 25 | color: color, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/shapes/marker_shape.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../states/selections/selection_state.dart'; 4 | import 'base_shape.dart'; 5 | import 'shape.dart'; 6 | 7 | class MarkerShape extends BaseShape { 8 | MarkerShape(double size) : super(0, 0, size, -1); 9 | 10 | @override 11 | Rect get rect => Rect.fromLTWH(x - width, y - width, width * 2, width * 2); 12 | 13 | @override 14 | void paint(Canvas canvas) => throw UnimplementedError(); 15 | 16 | @override 17 | SelectionState createSelectionState() => throw UnimplementedError(); 18 | } 19 | -------------------------------------------------------------------------------- /patterns/adapter/text_graphics/third_party_graphics_lib/wave.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:typed_data'; 3 | 4 | import 'graph_element.dart'; 5 | 6 | class Wave extends GraphElement { 7 | Wave({ 8 | required final int height, 9 | final int length = 50, 10 | final double waveStep = .8, 11 | }) { 12 | final list = []; 13 | for (var x = 0; x < length; x++) { 14 | final y = (height + cos(x / pi / waveStep) * height).toInt(); 15 | list.addAll([x, y]); 16 | } 17 | points = Int32List.fromList(list); 18 | } 19 | 20 | @override 21 | late Int32List points; 22 | } 23 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/app/shapes.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | import '../pattern/shape.dart'; 6 | 7 | class Shapes with IterableMixin { 8 | final List _shapes; 9 | 10 | Shapes(this._shapes); 11 | 12 | void add(Shape shape) { 13 | _shapes.add(shape); 14 | onAddShapeEvent._emit(); 15 | } 16 | 17 | @override 18 | Iterator get iterator => _shapes.iterator; 19 | 20 | final onAddShapeEvent = Event(); 21 | } 22 | 23 | class Event extends ChangeNotifier { 24 | void _emit() => notifyListeners(); 25 | } 26 | -------------------------------------------------------------------------------- /patterns/composite/image_editor/editor/image_editor.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | import '../shapes/compound_shape.dart'; 4 | import '../shapes/shape.dart'; 5 | 6 | typedef Graphics = Canvas; 7 | 8 | class ImageEditor { 9 | final _allShapes = CompoundShape(); 10 | 11 | void loadShapes(List shapes) { 12 | _allShapes 13 | ..clear() 14 | ..addAll(shapes); 15 | } 16 | 17 | String render() { 18 | final graphics = Graphics(_allShapes.width + 2, _allShapes.height + 2); 19 | _allShapes.paint(graphics); 20 | return graphics.toString(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/operations/concrete_visitor1.dart: -------------------------------------------------------------------------------- 1 | import '../elements/one.dart'; 2 | import '../elements/three.dart'; 3 | import '../elements/two.dart'; 4 | import '../pattern/visitor.dart'; 5 | 6 | class ConcreteVisitor1 implements Visitor { 7 | @override 8 | void visitOne(One one) { 9 | print('operation1: one (param1 = ${one.param1})'); 10 | } 11 | 12 | @override 13 | void visitTwo(Two two) { 14 | print('operation1: two (param2 = ${two.param2})'); 15 | } 16 | 17 | @override 18 | void visitThree(Three three) { 19 | print('operation1: three (param3 = ${three.param3})'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/operations/concrete_visitor2.dart: -------------------------------------------------------------------------------- 1 | import '../elements/one.dart'; 2 | import '../elements/three.dart'; 3 | import '../elements/two.dart'; 4 | import '../pattern/visitor.dart'; 5 | 6 | class ConcreteVisitor2 implements Visitor { 7 | @override 8 | void visitOne(One one) { 9 | print('operation2: one (param1 = ${one.param1})'); 10 | } 11 | 12 | @override 13 | void visitTwo(Two two) { 14 | print('operation2: two (param2 = ${two.param2})'); 15 | } 16 | 17 | @override 18 | void visitThree(Three three) { 19 | print('operation2: three (param3 = ${three.param3})'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/formats/console_format.dart: -------------------------------------------------------------------------------- 1 | import 'text_format.dart'; 2 | 3 | class ConsoleFormat extends TextFormat { 4 | final _buff = [' ']; 5 | 6 | static const colors = { 7 | 'red': '\x1b[31m', 8 | 'green': '\x1b[32m', 9 | 'blue': '\x1b[34m', 10 | }; 11 | 12 | var _fgColor = ''; 13 | var _end = ''; 14 | 15 | set color(String colorName) { 16 | _fgColor = colors[colorName] ?? ''; 17 | _end = _fgColor == '' ? '' : '\x1B[0m'; 18 | } 19 | 20 | void write(String text) => _buff.add('$_fgColor$text$_end'); 21 | 22 | @override 23 | String get content => _buff.join(' '); 24 | } 25 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/concrete_builder/concrete_builder_1.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/builder.dart'; 2 | import '../pattern/product.dart'; 3 | import '../product/concrete_product_1.dart'; 4 | 5 | class ConcreteBuilder1 implements Builder { 6 | @override 7 | void buildPart1() { 8 | _product.addLine('one'); 9 | } 10 | 11 | @override 12 | void buildPart2() { 13 | _product.addLine('two'); 14 | } 15 | 16 | @override 17 | void buildPart3() { 18 | _product.addLine('three'); 19 | } 20 | 21 | @override 22 | Product get result => _product; 23 | 24 | final _product = ConcreteProduct1('ConcreteBuilder1'); 25 | } 26 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/shapes/base_shape.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../pattern/shape.dart'; 4 | 5 | abstract class BaseShape implements Shape { 6 | @override 7 | double get x => _x; 8 | 9 | @override 10 | double get y => _y; 11 | 12 | @override 13 | final Color color; 14 | 15 | BaseShape({ 16 | required double x, 17 | required double y, 18 | required this.color, 19 | }) : _x = x, 20 | _y = y; 21 | 22 | @override 23 | void centerToFit() { 24 | _x -= width / 2; 25 | _y -= height / 2; 26 | } 27 | 28 | double _x; 29 | double _y; 30 | } 31 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/concrete_builder/concrete_builder_2.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/builder.dart'; 2 | import '../pattern/product.dart'; 3 | import '../product/concrete_product_2.dart'; 4 | 5 | class ConcreteBuilder2 implements Builder { 6 | @override 7 | void buildPart1() { 8 | _product.addLine('first'); 9 | } 10 | 11 | @override 12 | void buildPart2() { 13 | _product.addLine('second'); 14 | } 15 | 16 | @override 17 | void buildPart3() { 18 | _product.addLine('third'); 19 | } 20 | 21 | @override 22 | Product get result => _product; 23 | 24 | final _product = ConcreteProduct2('ConcreteBuilder2'); 25 | } 26 | -------------------------------------------------------------------------------- /patterns/strategy/view_strategy/main.dart: -------------------------------------------------------------------------------- 1 | import 'pattern/byte_context.dart'; 2 | import 'strategies/hex_view_strategy.dart'; 3 | import 'strategies/str_view_strategy.dart'; 4 | import 'strategies/zip_view_strategy.dart'; 5 | 6 | void main() { 7 | final byteList = ByteContext() 8 | ..add('Hello guru') 9 | ..add(123456789) 10 | ..add(3.1456564984); 11 | 12 | final strFormat = byteList.toStringView(StrViewStrategy()); 13 | final hexFormat = byteList.toStringView(HexViewStrategy()); 14 | final zipFormat = byteList.toStringView(ZipViewStrategy()); 15 | 16 | print(strFormat); 17 | print(hexFormat); 18 | print(zipFormat); 19 | } 20 | -------------------------------------------------------------------------------- /patterns/builder/cars/components/engine.dart: -------------------------------------------------------------------------------- 1 | /// EN: Just another feature of a car. 2 | /// 3 | /// RU: Одна из фишек автомобиля. 4 | class Engine { 5 | final double volume; 6 | 7 | double _mileage; 8 | 9 | double get mileage => _mileage; 10 | 11 | var _started = false; 12 | 13 | Engine(this.volume, this._mileage); 14 | 15 | void on() => _started = true; 16 | 17 | void off() => _started = false; 18 | 19 | bool get isStarted => _started; 20 | 21 | void go(double mileage) { 22 | if (_started) { 23 | _mileage += mileage; 24 | } else { 25 | print('Cannot go(), you must start engine first!'); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/shapes/rectangle_shape.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../states/selections/resizable_state.dart'; 4 | import '../states/selections/selection_state.dart'; 5 | import 'base_shape.dart'; 6 | 7 | class RectangleShape extends BaseShape { 8 | RectangleShape(super.x, super.y, super.width, super.height); 9 | 10 | @override 11 | SelectionState createSelectionState() { 12 | return ResizableState(selectedShape: this); 13 | } 14 | 15 | @override 16 | void paint(Canvas canvas) { 17 | canvas.drawRect( 18 | rect, 19 | Paint()..color = Colors.white, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: design_patterns_dart 2 | description: Dart examples for all classic GoF design patterns. 3 | version: 0.38.0 4 | homepage: https://refactoring.guru/design-patterns 5 | repository: https://github.com/RefactoringGuru/design-patterns-dart 6 | issue_tracker: https://github.com/RefactoringGuru/design-patterns-dart/issue 7 | 8 | environment: 9 | sdk: ">=2.17.0 <3.0.0" 10 | 11 | dependencies: 12 | collection: ^1.15.0 13 | flutter: 14 | sdk: flutter 15 | cupertino_icons: ^1.0.2 16 | material_design_icons_flutter: ^5.0.6595 17 | 18 | dev_dependencies: 19 | flutter_lints: ^2.0.0 20 | 21 | flutter: 22 | uses-material-design: true 23 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/filed_label.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class FieldLabel extends StatelessWidget { 4 | final String text; 5 | final Widget child; 6 | 7 | const FieldLabel({ 8 | Key? key, 9 | required this.text, 10 | required this.child, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Row( 16 | children: [ 17 | SizedBox(width: 10), 18 | Text('$text:'), 19 | SizedBox(width: 10), 20 | child, 21 | SizedBox(width: 20), 22 | ], 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /patterns/command/conceptual/command/add_text_command.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/command.dart'; 2 | import '../mut_str/mut_str.dart'; 3 | 4 | class AddTextCommand implements Command { 5 | final String addedText; 6 | final MutStr mutStr; 7 | 8 | AddTextCommand(this.addedText, this.mutStr); 9 | 10 | @override 11 | void execute() { 12 | additionPosition = mutStr.len; 13 | mutStr.push(addedText); 14 | } 15 | 16 | @override 17 | void undo() { 18 | if (additionPosition == null) { 19 | return; 20 | } 21 | 22 | mutStr.delete(additionPosition!, additionPosition! + addedText.length); 23 | } 24 | 25 | int? additionPosition; 26 | } 27 | -------------------------------------------------------------------------------- /patterns/composite/image_editor/shapes/rectangle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:design_patterns_dart/text_canvas.dart'; 4 | 5 | import '../editor/image_editor.dart'; 6 | import 'base_shape.dart'; 7 | 8 | class Rectangle extends BaseShape { 9 | @override 10 | final int width; 11 | 12 | @override 13 | final int height; 14 | 15 | Rectangle(int x, int y, this.width, this.height, Color color) 16 | : super(x, y, color); 17 | 18 | @override 19 | void paint(Graphics graphics) { 20 | super.paint(graphics); 21 | graphics 22 | ..translate = Point(x - 1, y - 1) 23 | ..rectangle(width - 2, height - 1); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/converters/html_converter.dart: -------------------------------------------------------------------------------- 1 | import '../formats/html_format.dart'; 2 | import 'converter.dart'; 3 | 4 | /// Builder 5 | class HtmlConverter extends Converter { 6 | @override 7 | HtmlFormat get result => _html..closeAllTags(); 8 | 9 | @override 10 | void writeColor(String color) { 11 | _html 12 | .addStyle(name: color, color: color) 13 | .openTagSpan(styleName: color) 14 | .writeText(color) 15 | .closeLastTag(); 16 | } 17 | 18 | @override 19 | void writeWord(String text) { 20 | _html.writeText('$text '); 21 | } 22 | 23 | final _html = HtmlFormat()..openTagP(); 24 | } 25 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/editor/manipulator.dart: -------------------------------------------------------------------------------- 1 | import '../shapes/shapes.dart'; 2 | 3 | mixin Manipulator implements Shapes { 4 | var _isMouseDown = false; 5 | 6 | @override 7 | void onMouseDown(double x, double y) { 8 | _isMouseDown = true; 9 | select(x, y); 10 | } 11 | 12 | @override 13 | void onMouseMove(double x, double y) { 14 | if (_isMouseDown) { 15 | activeShape?.dragTo(x, y); 16 | } 17 | } 18 | 19 | @override 20 | void onPointerWheel(double deltaX, double deltaY) { 21 | activeShape?.changeSize(deltaY / 5); 22 | } 23 | 24 | @override 25 | void onMouseUp() { 26 | _isMouseDown = false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /patterns/composite/image_editor/shapes/circle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:design_patterns_dart/text_canvas.dart'; 4 | 5 | import '../editor/image_editor.dart'; 6 | import 'base_shape.dart'; 7 | 8 | class Circle extends BaseShape { 9 | final int radius; 10 | 11 | Circle(int x, int y, this.radius, Color color) : super(x, y, color); 12 | 13 | @override 14 | int get width => radius * 2; 15 | 16 | @override 17 | int get height => radius * 2; 18 | 19 | @override 20 | void paint(Graphics graphics) { 21 | super.paint(graphics); 22 | graphics 23 | ..translate = Point(x + radius, y + radius) 24 | ..circle(radius); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/classic_app/classic_app.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../../../observer/app_observer/observer/app_observer.dart'; 4 | import 'repaint_event.dart'; 5 | import 'repaint_compatible.dart'; 6 | 7 | abstract class ClassicApp implements RepaintCompatible { 8 | final events = AppObserver(); 9 | 10 | void onMouseDown(double x, double y) {} 11 | 12 | void onMouseUp() {} 13 | 14 | void onMouseMove(double x, double y) {} 15 | 16 | void onPointerWheel(double deltaX, double deltaY) {} 17 | 18 | @override 19 | void repaint() { 20 | events.notify(RepaintEvent()); 21 | } 22 | 23 | void onPaint(Canvas canvas, Size canvasSize); 24 | } 25 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/color_reader/color_text_reader.dart: -------------------------------------------------------------------------------- 1 | import '../converters/converter.dart'; 2 | import '../formats/text_format.dart'; 3 | 4 | /// Director 5 | class ColorTextReader { 6 | final String text; 7 | 8 | ColorTextReader({required this.text}); 9 | 10 | T convert(Converter converter) { 11 | for (final word in text.split(' ')) { 12 | if (supportedColors.contains(word)) { 13 | converter.writeColor(word); 14 | } else { 15 | converter.writeWord(word); 16 | } 17 | } 18 | return converter.result; 19 | } 20 | 21 | final supportedColors = Set.unmodifiable(['red', 'green', 'blue']); 22 | } 23 | -------------------------------------------------------------------------------- /patterns/command/conceptual/command/insert_text_command.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/command.dart'; 2 | import '../mut_str/mut_str.dart'; 3 | 4 | class InsertTextCommand extends Command { 5 | final int pos; 6 | final String insertText; 7 | final MutStr mutStr; 8 | 9 | InsertTextCommand(this.insertText, this.mutStr, {required this.pos}); 10 | 11 | @override 12 | void execute() { 13 | _isNotExecute = false; 14 | mutStr.insert(pos + 1, insertText); 15 | } 16 | 17 | @override 18 | void undo() { 19 | if (_isNotExecute) { 20 | return; 21 | } 22 | 23 | mutStr.delete(pos + 1, insertText.length - 1); 24 | } 25 | 26 | bool _isNotExecute = true; 27 | } 28 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/miners/html_analyzer.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/data_miner.dart'; 2 | import '../utils/raw_data.dart'; 3 | 4 | class HTMLMiner extends DataMiner { 5 | HTMLMiner(super.fileName); 6 | 7 | @override 8 | StringTable parseData(RawData raw) { 9 | String regexString = r']+>(.+)<\/td>' 10 | r'\s+' 11 | r']+>(.+)<\/td>'; 12 | final regExp = RegExp(regexString); 13 | final matches = regExp.allMatches(raw); 14 | 15 | return [ 16 | for (final employer in matches) 17 | [ 18 | employer.group(1)!, 19 | employer.group(2)!, 20 | ] 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/main.dart: -------------------------------------------------------------------------------- 1 | import 'color_reader/color_text_reader.dart'; 2 | import 'converters/html_converter.dart'; 3 | import 'converters/json_converter.dart'; 4 | import 'converters/console_converter.dart'; 5 | 6 | void main() { 7 | final reader = ColorTextReader( 8 | text: 'I love looking at the blue sky, ' 9 | 'eating red apples, ' 10 | 'sitting on the green grass.', 11 | ); 12 | 13 | final html = reader.convert(HtmlConverter()); 14 | final json = reader.convert(JsonConverter()); 15 | final console = reader.convert(ConsoleConverter()); 16 | 17 | print( 18 | '$html,\n\n' 19 | '$json,\n\n' 20 | '$console,\n\n', 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/render_elements/render_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | import 'render_element.dart'; 4 | 5 | class RenderText extends RenderElement { 6 | RenderText(this.text, {required this.borderStyle}); 7 | 8 | final String text; 9 | final BorderStyle borderStyle; 10 | 11 | @override 12 | int get width => text.length + 2 + 2; 13 | 14 | @override 15 | int get height => 3; 16 | 17 | @override 18 | void render(Canvas dc) { 19 | if (borderStyle != BorderStyle.empty) { 20 | dc.border(width, height, borderStyle); 21 | } 22 | dc.text(text, widthCenter: width, heightCenter: height); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patterns/decorator/data_source_decoder/src/file_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'data_source.dart'; 4 | 5 | class FileDataSource implements DataSource { 6 | final String _name; 7 | 8 | FileDataSource(String name) : _name = thisPath(name); 9 | 10 | @override 11 | void writeData(String data) { 12 | File(_name).writeAsStringSync(data); 13 | } 14 | 15 | @override 16 | String readData() { 17 | return File(_name).readAsStringSync(); 18 | } 19 | } 20 | 21 | thisPath(name) => 22 | Platform.script.pathSegments 23 | .sublist(0, Platform.script.pathSegments.length - 1) 24 | .join(Platform.pathSeparator) + 25 | Platform.pathSeparator + 26 | name; 27 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/text/text_cursor_animation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | class TextCursorAnimation { 4 | final Duration speed; 5 | final void Function() onBlink; 6 | 7 | TextCursorAnimation({ 8 | required this.speed, 9 | required this.onBlink, 10 | }) { 11 | _timer = Timer.periodic(speed, (_) { 12 | _isShowCursor = !_isShowCursor; 13 | onBlink.call(); 14 | }); 15 | } 16 | 17 | bool get isVisible => _isShowCursor; 18 | 19 | void touch() { 20 | _isShowCursor = true; 21 | onBlink.call(); 22 | } 23 | 24 | void dispose() { 25 | _timer.cancel(); 26 | } 27 | 28 | bool _isShowCursor = true; 29 | late Timer _timer; 30 | } 31 | -------------------------------------------------------------------------------- /patterns/strategy/view_strategy/strategies/zip_view_strategy.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../pattern/byte_context.dart'; 4 | import 'str_view_strategy.dart'; 5 | 6 | class ZipViewStrategy extends StrViewStrategy { 7 | @override 8 | String out(ByteContext byteList) { 9 | final codes = super.out(byteList).codeUnits; 10 | final bytes = GZipCodec().encode(codes); 11 | final buf = StringBuffer(); 12 | 13 | var odd = 1; 14 | for (final byte in bytes) { 15 | final hexByte = byte.toRadixString(16).padLeft(2, '0'); 16 | buf.write('$hexByte '); 17 | 18 | if (odd++ % 16 == 0) { 19 | buf.writeln(); 20 | } 21 | } 22 | 23 | return buf.toString(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/tool_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../app/tools.dart'; 4 | import 'colors_tool_bar.dart'; 5 | import 'factories_tool_bar.dart'; 6 | 7 | class ToolPanel extends StatelessWidget { 8 | final Tools tools; 9 | const ToolPanel({Key? key, required this.tools}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Positioned( 14 | left: 12, 15 | top: 12, 16 | child: Column( 17 | children: [ 18 | FactoriesToolBar(tools: tools), 19 | SizedBox(height: 24), 20 | ColorsToolBar(tools: tools), 21 | ], 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/after/main.dart: -------------------------------------------------------------------------------- 1 | import 'elements/one.dart'; 2 | import 'elements/three.dart'; 3 | import 'elements/two.dart'; 4 | import 'operations/concrete_visitor1.dart'; 5 | import 'operations/concrete_visitor2.dart'; 6 | import 'pattern/element.dart'; 7 | import 'pattern/visitor.dart'; 8 | 9 | void main() { 10 | final list = createElements(); 11 | operation(list, ConcreteVisitor1()); 12 | operation(list, ConcreteVisitor2()); 13 | } 14 | 15 | Iterable createElements() { 16 | return [ 17 | One(), 18 | Two(), 19 | Three(), 20 | ]; 21 | } 22 | 23 | void operation(Iterable elements, Visitor visitor) { 24 | for (final e in elements) { 25 | e.accept(visitor); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | 4 | import '../classic_app/classic_app.dart'; 5 | import 'classic_app_render_object.dart'; 6 | 7 | class ClassicAppAdapterWidget extends LeafRenderObjectWidget { 8 | final ClassicApp classicApp; 9 | 10 | ClassicAppAdapterWidget({required this.classicApp}); 11 | 12 | @override 13 | RenderObject createRenderObject(BuildContext context) { 14 | return ClassicAppRenderObject(classicApp); 15 | } 16 | 17 | @override 18 | void updateRenderObject( 19 | BuildContext context, 20 | covariant ClassicAppRenderObject renderObject, 21 | ) { 22 | renderObject.classicApp = classicApp; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patterns/adapter/text_graphics/engine/shape_engine.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:design_patterns_dart/text_canvas.dart'; 4 | 5 | import '../shapes/shape.dart'; 6 | 7 | class ShapeEngine { 8 | final List shapes; 9 | final int width; 10 | final int height; 11 | 12 | ShapeEngine({ 13 | required this.width, 14 | required this.height, 15 | required this.shapes, 16 | }); 17 | 18 | String render() { 19 | final can = Canvas(width, height, lineStretch: 3); 20 | for (final Shape shape in shapes) { 21 | can 22 | ..translate = Point(shape.x, shape.y) 23 | ..penColor = shape.color; 24 | shape.draw(can); 25 | } 26 | return can.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/dialogs_factory/dialog.dart: -------------------------------------------------------------------------------- 1 | import '../buttons/button.dart'; 2 | 3 | abstract class Dialog { 4 | void renderWindow() { 5 | /// EN: ... other code ... 6 | /// 7 | /// RU: ... остальной код диалога ... 8 | 9 | Button okButton = createButton(() { 10 | print('Click! Button says - "Hello World!"'); 11 | }); 12 | okButton.render(); 13 | } 14 | 15 | /// EN: Subclasses will override this method in order to create specific 16 | /// button objects. 17 | /// 18 | /// RU: Подклассы будут переопределять этот метод, чтобы создавать конкретные 19 | /// объекты продуктов, разные для каждой фабрики. 20 | Button createButton(void Function() onClick); 21 | } 22 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/diagram/convert_product_to_render_element.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | import '../products/product.dart'; 4 | import '../products/product_leaf.dart'; 5 | import '../render_elements/render_element.dart'; 6 | import '../render_elements/render_text.dart'; 7 | 8 | 9 | extension ConvertProductToDiagram on Product { 10 | RenderElement toRenderElement() { 11 | return RenderText( 12 | content, 13 | borderStyle: borderStyleBySize(), 14 | ); 15 | } 16 | 17 | BorderStyle borderStyleBySize() { 18 | if (this is ProductLeaf) { 19 | return BorderStyle.empty; 20 | } else { 21 | return BorderStyle.single; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patterns/builder/cars/builders/builder.dart: -------------------------------------------------------------------------------- 1 | import '../cars/car_type.dart'; 2 | import '../components/engine.dart'; 3 | import '../director/gps_navigation.dart'; 4 | import '../director/transmission.dart'; 5 | import '../director/trip_computer.dart'; 6 | 7 | /// EN: Builder interface defines all possible ways to configure a product. 8 | /// 9 | /// RU: Интерфейс Строителя объявляет все возможные этапы и шаги конфигурации 10 | /// продукта. 11 | abstract class Builder { 12 | set carType(CarType type); 13 | 14 | set seats(int seats); 15 | 16 | set engine(Engine engine); 17 | 18 | set transmission(Transmission transmission); 19 | 20 | set tripComputer(TripComputer tripComputer); 21 | 22 | set gpsNavigator(GPSNavigator gpsNavigator); 23 | } 24 | -------------------------------------------------------------------------------- /patterns/observer/subscriber_flutter_widget/widgets/hash_user_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../../app_observer/observer/app_observer.dart'; 4 | import '../events/new_hash_event.dart'; 5 | import '../subscriber/subscriber_widget.dart'; 6 | 7 | class HashUserWidget extends StatelessWidget { 8 | final AppObserver observer; 9 | 10 | const HashUserWidget({Key? key, required this.observer}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return SubscriberWidget( 15 | observer: observer, 16 | builder: (context, event) { 17 | return Text( 18 | event?.newHash ?? 'Hash no generated', 19 | ); 20 | }, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /patterns/prototype/shapes/main.dart: -------------------------------------------------------------------------------- 1 | import 'shape/circle.dart'; 2 | import 'shape/rectangle.dart'; 3 | import 'shape/shape.dart'; 4 | 5 | void main() { 6 | final originalShapes = [ 7 | Rectangle( 8 | x: 100, 9 | y: 100, 10 | width: 500, 11 | height: 100, 12 | color: '0xfff', 13 | ), 14 | Circle( 15 | x: 20, 16 | y: 30, 17 | radius: 100, 18 | color: '0xf0f', 19 | ), 20 | ]; 21 | 22 | businessLogic(originalShapes); 23 | } 24 | 25 | void businessLogic(List originalShapes) { 26 | final cloningShapes = [ 27 | for (final shape in originalShapes) shape.clone(), 28 | ]; 29 | 30 | print('Origin shapes: $originalShapes'); 31 | print('Cloning shapes: $cloningShapes'); 32 | } 33 | -------------------------------------------------------------------------------- /patterns/command/text_editor/commands/copy_command.dart: -------------------------------------------------------------------------------- 1 | library copy_past; 2 | 3 | import '../application/application.dart'; 4 | import 'command.dart'; 5 | 6 | part 'cut_command.dart'; 7 | 8 | class CopyCommand extends Command { 9 | String _copyText = ''; 10 | 11 | CopyCommand(Application app) : super(app); 12 | 13 | @override 14 | bool get isSaveHistory => false; 15 | 16 | @override 17 | void execute() { 18 | if (!app.editor.cursor.isTextSelected) { 19 | return; 20 | } 21 | 22 | _copyText = app.editor.selectedText; 23 | app.clipboard = _copyText; 24 | } 25 | 26 | @override 27 | void undo() {} 28 | 29 | @override 30 | String toString() { 31 | return 'Copy( ' 32 | 'text: "$_copyText" )'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /patterns/adapter/text_graphics/shapes/graph_element_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | import '../third_party_graphics_lib/graph_element.dart' as third_party; 4 | import 'shape.dart'; 5 | 6 | class GraphElementAdapter extends Shape { 7 | final third_party.GraphElement graphElement; 8 | 9 | GraphElementAdapter({ 10 | required int x, 11 | required int y, 12 | required Color color, 13 | required this.graphElement, 14 | }) : super(x, y, color); 15 | 16 | @override 17 | void draw(Canvas can) { 18 | final points = graphElement.points; 19 | can.moveTo(points[0], points[1]); 20 | for (var i = 0; i < points.length; i += 2) { 21 | can.lineTo(points[i], points[i + 1]); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/inner_radius_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../shapes/circle_shape.dart'; 4 | import 'inner_radius_markers/inner_radius_marker_state.dart'; 5 | import 'resizable_state.dart'; 6 | 7 | class InnerRadiusState extends ResizableState { 8 | InnerRadiusState({required super.selectedShape}) { 9 | addChildren([ 10 | InnerRadiusMarkerState(parentState: this), 11 | ]); 12 | } 13 | 14 | @override 15 | void paint(Canvas canvas) { 16 | context.paintStyle.paintRadiusLine(selectedShape, canvas); 17 | super.paint(canvas); 18 | } 19 | 20 | @override 21 | String toString() { 22 | return '${super.toString()} + Inner Radius State'; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patterns/strategy/reservation_cargo_spaces/application/application.dart: -------------------------------------------------------------------------------- 1 | import '../partners/cargo.dart'; 2 | import '../partners/voyage.dart'; 3 | import '../policy/overbooking_policy.dart'; 4 | import 'order_confirmation_sequence.dart'; 5 | 6 | class Application { 7 | final OverbookingPolicy overbookingPolicy; 8 | final orderConfirmationSequence = OrderConfirmationSequence(); 9 | 10 | Application(this.overbookingPolicy); 11 | 12 | void makeBooking(Cargo cargo, Voyage voyage) { 13 | if (overbookingPolicy.isAllowed(cargo, voyage)) { 14 | final confirmation = orderConfirmationSequence.next(); 15 | voyage.addCargo(cargo, confirmation); 16 | } else { 17 | throw 'The weight of the cargo exceeds the permissible norm.'; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/editor/editor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; 4 | import '../memento_pattern/originator.dart'; 5 | import '../shapes/shapes.dart'; 6 | import 'manipulator.dart'; 7 | 8 | class Editor extends ClassicApp 9 | with Manipulator, Shapes, BackupOriginator, RecoveryOriginator { 10 | @override 11 | void onPaint(Canvas canvas, Size canvasSize) { 12 | _paintBackground(canvas, canvasSize); 13 | paintShapes(canvas); 14 | activeShape?.paintSelectionBox(canvas); 15 | } 16 | 17 | void _paintBackground(Canvas canvas, Size canvasSize) { 18 | canvas.drawRect( 19 | Offset.zero & canvasSize, 20 | Paint()..color = Color(0xff404040), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /patterns/singleton/conceptual/README.md: -------------------------------------------------------------------------------- 1 | # Singleton Pattern 2 | Singleton is a creational design pattern that lets you ensure that a class has only one instance, 3 | while providing a global access point to this instance. 4 | 5 | Tutorial: [here](https://refactoring.guru/design-patterns/singleton). 6 | 7 | ### Diagram: 8 | ![Singleton Pattern Diagram](https://user-images.githubusercontent.com/8049534/182938119-78a21534-5751-4dea-afa3-8acaec46eed9.png) 9 | 10 | ### Client code: 11 | ```dart 12 | void main() { 13 | // dart style 14 | Singleton().doSome(); 15 | Singleton().doSome(); 16 | 17 | // standard style 18 | Singleton.instance.doSome(); 19 | } 20 | ``` 21 | 22 | ### Output: 23 | ``` 24 | Create singleton once. 25 | doSome() 26 | doSome() 27 | doSome() 28 | ``` 29 | -------------------------------------------------------------------------------- /patterns/visitor/shapes_exporter/main.dart: -------------------------------------------------------------------------------- 1 | import 'shapes/circle.dart'; 2 | import 'shapes/compound_shape.dart'; 3 | import 'shapes/dot.dart'; 4 | import 'shapes/rectangle.dart'; 5 | import 'visitor/xml_export_visitor.dart'; 6 | 7 | void main() { 8 | final compoundShape = CompoundShape( 9 | x: 30, 10 | y: 45, 11 | children: [ 12 | Rectangle(x: 10, y: 10, width: 100, height: 100), 13 | Circle(xCenter: 300, yCenter: 20, radius: 35), 14 | Dot(x: 60, y: 60), 15 | CompoundShape( 16 | x: 5, 17 | y: 5, 18 | children: [ 19 | Dot(x: 15, y: 15), 20 | Dot(x: 20, y: 20), 21 | ], 22 | ), 23 | ], 24 | ); 25 | 26 | final xml = XMLExportVisitor().export(compoundShape); 27 | print(xml); 28 | } 29 | -------------------------------------------------------------------------------- /patterns/adapter/text_graphics/third_party_graphics_lib/star.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'graph_element.dart'; 4 | 5 | class Star extends GraphElement { 6 | final int size; 7 | 8 | Star({required this.size}) { 9 | final list = []; 10 | final p1 = (size * .1).toInt(), 11 | p3 = (size * .3).toInt(), 12 | p4 = (size * .4).toInt(), 13 | p7 = (size * .7).toInt(), 14 | p8 = (size * .8).toInt(), 15 | p9 = (size * .9).toInt(); 16 | list.addAll([p1, p9]); 17 | list.addAll([p4, 0]); 18 | list.addAll([p7, p9]); 19 | list.addAll([0, p3]); 20 | list.addAll([p8, p3]); 21 | list.addAll([p1, p9]); 22 | points = Int32List.fromList(list); 23 | } 24 | 25 | @override 26 | late Int32List points; 27 | } 28 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/operations/operation.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/context.dart'; 2 | import '../pattern/expression.dart'; 3 | 4 | abstract class Operation implements Expression { 5 | final Expression expression1; 6 | final Expression expression2; 7 | 8 | Operation(this.expression1, this.expression2); 9 | 10 | bool operation(bool a, bool b); 11 | 12 | @override 13 | bool evaluate(Context context) { 14 | final a = expression1.evaluate(context); 15 | final b = expression2.evaluate(context); 16 | return operation(a, b); 17 | } 18 | 19 | @override 20 | String toDebugString(Context context) { 21 | final a = expression1.toDebugString(context); 22 | final b = expression2.toDebugString(context); 23 | return '$b $runtimeType $a'; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/widgets/composite/named_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class NamedPanel extends StatelessWidget { 4 | final String name; 5 | final List children; 6 | 7 | NamedPanel({ 8 | Key? key, 9 | required this.name, 10 | required this.children, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | crossAxisAlignment: CrossAxisAlignment.start, 17 | children: [ 18 | Text( 19 | name, 20 | style: TextStyle( 21 | fontSize: 12, 22 | fontWeight: FontWeight.bold, 23 | ), 24 | ), 25 | SizedBox(height: 15), 26 | ...children, 27 | ], 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/resizable_markers/bottom_right_marker_state.dart: -------------------------------------------------------------------------------- 1 | import '../../_/marker.dart'; 2 | 3 | class BottomRightMarkerState extends Marker { 4 | BottomRightMarkerState({ 5 | required super.parentState, 6 | }); 7 | 8 | @override 9 | void mouseDragAction(double x, double y) { 10 | selectedShape.resize( 11 | x - selectedShape.x, 12 | y - selectedShape.y, 13 | ); 14 | } 15 | 16 | @override 17 | void updatePosition() { 18 | final width = selectedShape.x + selectedShape.width; 19 | final height = selectedShape.y + selectedShape.height; 20 | markerShape.move(width, height); 21 | } 22 | 23 | @override 24 | String toString() { 25 | return '${parentState.toString()} + Bottom Right Marker State'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /patterns/bridge/devices_remote_control/main.dart: -------------------------------------------------------------------------------- 1 | import 'devices/device.dart'; 2 | import 'devices/empty_device.dart'; 3 | import 'devices/radio.dart'; 4 | import 'devices/tv.dart'; 5 | import 'remotes/basic_advance_remote.dart'; 6 | 7 | void main() { 8 | testDevice(Tv()); 9 | testDevice(Radio()); 10 | testDevice(EmptyDevice()); 11 | } 12 | 13 | void testDevice(Device device) { 14 | print(''.padRight(36, '=')); 15 | print(device.runtimeType); 16 | print("Tests with basic remote."); 17 | final basicRemote = BasicRemote.fromDevice(device); 18 | basicRemote.power(); 19 | device.printStatus(); 20 | 21 | print("Tests with advanced remote."); 22 | final advancedRemote = AdvancedRemote.fromDevice(device); 23 | advancedRemote.power(); 24 | advancedRemote.mute(); 25 | device.printStatus(); 26 | } 27 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/render_elements/render_position.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:design_patterns_dart/text_canvas/canvas.dart'; 4 | 5 | import 'render_element.dart'; 6 | 7 | class RenderPosition extends RenderElement { 8 | RenderPosition({ 9 | required this.x, 10 | required this.y, 11 | required this.child, 12 | }); 13 | 14 | final int x; 15 | final int y; 16 | final RenderElement child; 17 | 18 | @override 19 | int get height => child.height; 20 | 21 | @override 22 | int get width => child.width; 23 | 24 | @override 25 | void render(Canvas dc) { 26 | final oldTranslate = dc.translate; 27 | dc.translate = Point(oldTranslate.x + x, oldTranslate.y + y); 28 | child.render(dc); 29 | dc.translate = oldTranslate; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /patterns/composite/image_editor/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | import 'editor/image_editor.dart'; 4 | import 'shapes/circle.dart'; 5 | import 'shapes/compound_shape.dart'; 6 | import 'shapes/dot.dart'; 7 | import 'shapes/rectangle.dart'; 8 | 9 | void main() { 10 | final editor = ImageEditor(); 11 | editor.loadShapes([ 12 | Circle(1, 1, 4, Color.grey), 13 | CompoundShape([ 14 | Circle(12, 12, 6, Color.dark), 15 | Dot(12 + 6, 12 + 6, Color.dark), 16 | ]) 17 | ..select(), 18 | CompoundShape([ 19 | Rectangle(31, 31, 10, 10, Color.black), 20 | Dot(28, 28, Color.grey), 21 | Dot(40, 28, Color.grey), 22 | Dot(40, 41, Color.grey), 23 | Dot(28, 41, Color.grey), 24 | ]), 25 | ]); 26 | print(editor.render()); 27 | } 28 | -------------------------------------------------------------------------------- /patterns/builder/cars/cars/car.dart: -------------------------------------------------------------------------------- 1 | import '../components/engine.dart'; 2 | import '../director/gps_navigation.dart'; 3 | import '../director/transmission.dart'; 4 | import '../director/trip_computer.dart'; 5 | import 'car_type.dart'; 6 | 7 | /// EN: Car is a product class. 8 | /// 9 | /// RU: Автомобиль — это класс продукта. 10 | class Car { 11 | final CarType carType; 12 | 13 | final int seats; 14 | 15 | final Engine engine; 16 | 17 | final Transmission transmission; 18 | 19 | final TripComputer tripComputer; 20 | 21 | final GPSNavigator gpsNavigator; 22 | 23 | final double fuel = 0.0; 24 | 25 | Car( 26 | this.carType, 27 | this.seats, 28 | this.engine, 29 | this.transmission, 30 | this.tripComputer, 31 | this.gpsNavigator, 32 | ) { 33 | tripComputer.car = this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /patterns/chain_of_responsibility/server_middleware/middleware/user_exists_middleware.dart: -------------------------------------------------------------------------------- 1 | import '../server/server.dart'; 2 | 3 | class UserExistsMiddleware extends Middleware { 4 | UserExistsMiddleware({Middleware? next}) : super(next: next); 5 | 6 | @override 7 | bool check(String email, String password) { 8 | if (server == null) { 9 | return false; 10 | } 11 | 12 | if (!server!.hasEmail(email)) { 13 | print('UserExistsMiddleware: this email($email) is not registered!'); 14 | return false; 15 | } 16 | 17 | if (!server!.isValidPassword(email, password)) { 18 | print('UserExistsMiddleware: wrong password!'); 19 | return false; 20 | } 21 | 22 | print('UserExistsMiddleware: user has been validated'); 23 | 24 | return checkNext(email, password); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/primitive/theme_property.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ThemeProperty extends StatelessWidget { 4 | final Widget child; 5 | 6 | const ThemeProperty({ 7 | Key? key, 8 | required this.child, 9 | }) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Material( 14 | color: Colors.transparent, 15 | textStyle: TextStyle( 16 | color: Colors.white, 17 | fontSize: 16, 18 | fontFamily: 'Arial', 19 | ), 20 | child: Theme( 21 | data: ThemeData( 22 | primarySwatch: Colors.pink, 23 | unselectedWidgetColor: Colors.grey, // Your color 24 | ), 25 | child: child, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/move_state.dart: -------------------------------------------------------------------------------- 1 | import 'selection_state.dart'; 2 | 3 | class MoveState extends SelectionState { 4 | final double startX; 5 | final double startY; 6 | 7 | MoveState({ 8 | required double startX, 9 | required double startY, 10 | required super.selectedShape, 11 | }) : startX = startX - selectedShape.x, 12 | startY = startY - selectedShape.y; 13 | 14 | @override 15 | void mouseMove(double x, double y) { 16 | selectedShape.move(x - startX, y - startY); 17 | context.update(); 18 | } 19 | 20 | @override 21 | void mouseUp() { 22 | context.changeState( 23 | selectedShape.createSelectionState(), 24 | ); 25 | } 26 | 27 | @override 28 | String toString() { 29 | return '${super.toString()} + Moving State'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /patterns/builder/color_text_format/converters/json_converter.dart: -------------------------------------------------------------------------------- 1 | import '../formats/json_format.dart'; 2 | import 'converter.dart'; 3 | 4 | class JsonConverter extends Converter { 5 | @override 6 | JsonFormat get result { 7 | _closeTextBuffer(); 8 | return _json; 9 | } 10 | 11 | final _json = JsonFormat(); 12 | 13 | @override 14 | void writeColor(String color) { 15 | _closeTextBuffer(); 16 | _json.add({ 17 | 'color': color, 18 | }); 19 | } 20 | 21 | @override 22 | void writeWord(String text) => _textBuffer.write('$text '); 23 | 24 | final _textBuffer = StringBuffer(); 25 | 26 | void _closeTextBuffer() { 27 | if (_textBuffer.isNotEmpty) { 28 | _json.add({ 29 | 'text': _textBuffer.toString(), 30 | }); 31 | _textBuffer.clear(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/resizable_markers/top_left_marker_state.dart: -------------------------------------------------------------------------------- 1 | import '../../_/marker.dart'; 2 | 3 | class TopLeftMarkerState extends Marker { 4 | TopLeftMarkerState({ 5 | required super.parentState, 6 | }); 7 | 8 | @override 9 | void mouseDragAction(double x, double y) { 10 | final newWidth = selectedShape.width + selectedShape.x - x; 11 | final newHeight = selectedShape.height + selectedShape.y - y; 12 | 13 | selectedShape 14 | ..resize(newWidth, newHeight) 15 | ..move(x, y); 16 | } 17 | 18 | @override 19 | void updatePosition() { 20 | markerShape.move( 21 | selectedShape.x, 22 | selectedShape.y, 23 | ); 24 | } 25 | 26 | @override 27 | String toString() { 28 | return '${parentState.toString()} + Top Left Marker State'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/text_resize_state.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../../shapes/text_shape.dart'; 4 | import '../_/sub_states/parent_state.dart'; 5 | import 'text_edit_state.dart'; 6 | import 'text/text_size_marker_state.dart'; 7 | 8 | class TextResizeState extends ParentState { 9 | TextResizeState({required super.selectedShape}) { 10 | addChildren([ 11 | TextSizeMarkerState(parentState: this), 12 | ]); 13 | } 14 | 15 | @override 16 | void mouseDoubleClick(double x, double y) { 17 | context.changeState( 18 | TextEditState( 19 | startPointer: Offset(x, y), 20 | selectedShape: selectedShape, 21 | ), 22 | ); 23 | } 24 | 25 | @override 26 | String toString() { 27 | return '${super.toString()} + Text Resize State'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /patterns/state/three_state/README.md: -------------------------------------------------------------------------------- 1 | # State Pattern 2 | State is a behavioral design pattern that lets an object alter its behavior when its internal state 3 | changes. It appears as if the object changed its class. 4 | 5 | Tutorial: [here](https://refactoring.guru/design-patterns/state). 6 | 7 | ### Diagram: 8 | ![image](https://user-images.githubusercontent.com/8049534/172001291-8d87a3c8-b694-45c3-bd46-40211cd9ac45.png) 9 | 10 | ### Client code: 11 | ```dart 12 | void main() { 13 | final switcher = Switcher( 14 | initState: One(), 15 | ); 16 | 17 | switcher.call(); // call(1): One 18 | switcher.call(); // call(2): Two 19 | switcher.call(); // call(3): Three 20 | switcher.call(); // call:(4) One 21 | } 22 | ``` 23 | 24 | ### Output: 25 | ``` 26 | call(1): One 27 | call(2): Two 28 | call(3): Three 29 | call(4): One 30 | ``` 31 | -------------------------------------------------------------------------------- /patterns/command/text_editor/commands/move_command.dart: -------------------------------------------------------------------------------- 1 | import '../application/application.dart'; 2 | import 'command.dart'; 3 | 4 | class MoveCommand extends Command { 5 | final int _positionTo; 6 | int? _positionFrom; 7 | 8 | MoveCommand(Application app, this._positionTo) : super(app); 9 | 10 | @override 11 | bool get isSaveHistory => false; 12 | 13 | @override 14 | void execute() { 15 | _positionFrom = app.editor.cursor.position; 16 | app.editor.cursorPosition = _positionTo; 17 | } 18 | 19 | @override 20 | void undo() { 21 | if (_positionFrom == null) { 22 | return; 23 | } 24 | 25 | app.editor.cursorPosition = _positionFrom!; 26 | } 27 | 28 | @override 29 | String toString() { 30 | return 'Move( ' 31 | 'from: $_positionFrom, ' 32 | 'to: $_positionTo )'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /patterns/decorator/data_source_decoder/main.dart: -------------------------------------------------------------------------------- 1 | import 'src/compression_decorator.dart'; 2 | import 'src/data_source.dart'; 3 | import 'src/data_source_decorator.dart'; 4 | import 'src/encryption_decorator.dart'; 5 | import 'src/file_data_source.dart'; 6 | 7 | void main() { 8 | final records = 'Name,Salary\nJohn Smith,100000\nSteven Jobs,912000'; 9 | 10 | // Create encrypt file 11 | DataSourceDecorator encoded = CompressionDecorator( 12 | EncryptionDecorator( 13 | FileDataSource('Secret.txt'), 14 | ), 15 | ); 16 | encoded.writeData(records); 17 | 18 | DataSource plain = FileDataSource('Secret.txt'); 19 | print("- Input ----------------"); 20 | print(records); 21 | print("\n- Encoded --------------"); 22 | print(plain.readData()); 23 | print("\n- Decoded --------------"); 24 | print(encoded.readData()); 25 | } 26 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/resizable_markers/bottom_left_marker_state.dart: -------------------------------------------------------------------------------- 1 | import '../../_/marker.dart'; 2 | 3 | class BottomLeftMarkerState extends Marker { 4 | BottomLeftMarkerState({ 5 | required super.parentState, 6 | }); 7 | 8 | @override 9 | void mouseDragAction(double x, double y) { 10 | final newX = selectedShape.width + selectedShape.x - x; 11 | final newY = y - selectedShape.y; 12 | 13 | selectedShape 14 | ..resize(newX, newY) 15 | ..move(x, selectedShape.y); 16 | } 17 | 18 | @override 19 | void updatePosition() { 20 | markerShape.move( 21 | selectedShape.x, 22 | selectedShape.y + selectedShape.height, 23 | ); 24 | } 25 | 26 | @override 27 | String toString() { 28 | return '${parentState.toString()} + Bottom Left Marker State'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/resizable_markers/top_right_marker_state.dart: -------------------------------------------------------------------------------- 1 | import '../../_/marker.dart'; 2 | 3 | class TopRightMarkerState extends Marker { 4 | TopRightMarkerState({ 5 | required super.parentState, 6 | }); 7 | 8 | @override 9 | void mouseDragAction(double x, double y) { 10 | final width = x - selectedShape.x; 11 | final height = selectedShape.height + selectedShape.y - y; 12 | 13 | selectedShape 14 | ..resize(width, height) 15 | ..move(selectedShape.x, y); 16 | } 17 | 18 | @override 19 | void updatePosition() { 20 | markerShape.move( 21 | selectedShape.x + selectedShape.width, 22 | selectedShape.y, 23 | ); 24 | } 25 | 26 | @override 27 | String toString() { 28 | return '${parentState.toString()} + Top Right Marker State'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /patterns/decorator/data_source_decoder/src/encryption_decorator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'data_source.dart'; 4 | import 'data_source_decorator.dart'; 5 | 6 | class EncryptionDecorator extends DataSourceDecorator { 7 | EncryptionDecorator(DataSource source) : super(source); 8 | 9 | @override 10 | void writeData(String data) { 11 | super.writeData(_encode(data)); 12 | } 13 | 14 | @override 15 | String readData() { 16 | return _decode(super.readData()); 17 | } 18 | 19 | String _encode(String data) { 20 | final encrypt = data.codeUnits.map((e) => e + 1).toList(); 21 | return base64.encode(encrypt); 22 | } 23 | 24 | String _decode(String data) { 25 | final encrypt = base64.decode(data); 26 | final decrypt = encrypt.map((e) => e - 1).toList(); 27 | return String.fromCharCodes(decrypt); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/text/keyboard_actions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class KeyboardActions { 4 | final Map actions; 5 | final void Function(String) inputCharAction; 6 | 7 | KeyboardActions({ 8 | required this.actions, 9 | required this.inputCharAction, 10 | }); 11 | 12 | void keyDown(KeyEvent keyEvent) { 13 | final isNotKeyDown = 14 | !(keyEvent is KeyDownEvent || keyEvent is KeyRepeatEvent); 15 | 16 | if (isNotKeyDown) { 17 | return; 18 | } 19 | 20 | final foundEvent = actions[keyEvent.physicalKey]; 21 | 22 | if (foundEvent != null) { 23 | foundEvent.call(); 24 | return; 25 | } 26 | 27 | if (keyEvent.character != null) { 28 | inputCharAction.call(keyEvent.character!); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/selection_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../shapes/shape.dart'; 4 | import '../free_sate.dart'; 5 | 6 | class SelectionState extends FreeState { 7 | final TShape selectedShape; 8 | 9 | SelectionState({required this.selectedShape}); 10 | 11 | @override 12 | void mouseDown(double x, double y) { 13 | final isShapeNotSelected = !tryToSelectAndStartMovingShape(x, y); 14 | 15 | if (isShapeNotSelected) { 16 | context.changeState(FreeState()); 17 | } 18 | } 19 | 20 | @override 21 | void paint(Canvas canvas) { 22 | super.paint(canvas); 23 | context.paintStyle.paintSelection(canvas, selectedShape); 24 | } 25 | 26 | @override 27 | String toString() { 28 | return '${super.toString()} + Selection State'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /patterns/command/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | import 'mut_str/mut_str.dart'; 2 | import 'command/add_text_command.dart'; 3 | import 'command/insert_text_command.dart'; 4 | 5 | void main() { 6 | final mutStr = MutStr(); 7 | 8 | final input1 = AddTextCommand('One', mutStr); 9 | final input2 = AddTextCommand('Three', mutStr); 10 | final input3 = InsertTextCommand(' Two ', mutStr, pos: 2); 11 | 12 | input1.execute(); 13 | print('text = $mutStr'); // mutStr = "One" 14 | 15 | input2.execute(); 16 | print('text = $mutStr'); // mutStr = "OneThree" 17 | 18 | input3.execute(); 19 | print('text = $mutStr'); // mutStr = "One Two Three" 20 | 21 | input3.undo(); 22 | print('text = $mutStr'); // mutStr = "OneThree" 23 | 24 | input2.undo(); 25 | print('text = $mutStr'); // mutStr = "One " 26 | 27 | input1.undo(); 28 | print('text = $mutStr'); // mutStr = "" 29 | } 30 | -------------------------------------------------------------------------------- /patterns/memento/conceptual/app/command.dart: -------------------------------------------------------------------------------- 1 | import 'editor.dart'; 2 | import 'snapshot.dart'; 3 | 4 | /// EN: A command object can act as a caretaker. In that case, the 5 | /// command gets a memento just before it changes the 6 | /// originator's state. When undo is requested, it restores the 7 | /// originator's state from a memento. 8 | /// 9 | /// RU: Опекуном может выступать класс команд (см. паттерн Команда). 10 | /// В этом случае команда сохраняет снимок состояния объекта- 11 | /// получателя, перед тем как передать ему своё действие. А в 12 | /// случае отмены команда вернёт объект в прежнее состояние. 13 | class Command { 14 | Snapshot _backup; 15 | 16 | Command._(this._backup); 17 | 18 | factory Command.makeBackup(Editor editor) { 19 | return Command._(editor.createSnapshot()); 20 | } 21 | 22 | void undo() { 23 | _backup.restore(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/app/shapes.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | 6 | import '../shapes/shape.dart'; 7 | 8 | class Shapes with IterableMixin { 9 | final List _shapes; 10 | 11 | Shapes(List shapes) : _shapes = shapes; 12 | 13 | void add(Shape shape) { 14 | _shapes.add(shape); 15 | onChange._emit(); 16 | } 17 | 18 | @override 19 | Iterator get iterator => _shapes.iterator; 20 | 21 | final onChange = Event(); 22 | 23 | Shape? findShapeByCoordinates(x, y) { 24 | for (final shape in _shapes.reversed) { 25 | if (shape.rect.contains(Offset(x, y))) { 26 | return shape; 27 | } 28 | } 29 | 30 | return null; 31 | } 32 | } 33 | 34 | class Event extends ChangeNotifier { 35 | void _emit() => notifyListeners(); 36 | } 37 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/free_sate.dart: -------------------------------------------------------------------------------- 1 | import '../pattern/manipulator.dart'; 2 | import '_/mixins/hover_shape_mixin.dart'; 3 | import 'selections/move_state.dart'; 4 | 5 | class FreeState extends ManipulationState with HoverShapeMixin { 6 | @override 7 | void mouseDown(double x, double y) { 8 | tryToSelectAndStartMovingShape(x, y); 9 | } 10 | 11 | bool tryToSelectAndStartMovingShape(double x, double y) { 12 | final selectedShape = context.shapes.findShapeByCoordinates(x, y); 13 | 14 | if (selectedShape == null) { 15 | return false; 16 | } 17 | 18 | context.changeState( 19 | MoveState( 20 | startX: x, 21 | startY: y, 22 | selectedShape: selectedShape, 23 | ), 24 | ); 25 | 26 | return true; 27 | } 28 | 29 | @override 30 | String toString() { 31 | return 'Free State'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/resizable_state.dart: -------------------------------------------------------------------------------- 1 | import 'resizable_markers/bottom_left_marker_state.dart'; 2 | import 'resizable_markers/bottom_right_marker_state.dart'; 3 | import 'resizable_markers/top_left_marker_state.dart'; 4 | import 'resizable_markers/top_right_marker_state.dart'; 5 | import '../_/sub_states/parent_state.dart'; 6 | import '../../shapes/shape.dart'; 7 | 8 | class ResizableState extends ParentState { 9 | ResizableState({required super.selectedShape}) { 10 | addChildren([ 11 | TopLeftMarkerState(parentState: this), 12 | TopRightMarkerState(parentState: this), 13 | BottomRightMarkerState(parentState: this), 14 | BottomLeftMarkerState(parentState: this), 15 | ]); 16 | } 17 | 18 | @override 19 | String toString() { 20 | return '${super.toString()} + Resizable State'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /patterns/bridge/devices_remote_control/devices/empty_device.dart: -------------------------------------------------------------------------------- 1 | import 'device.dart'; 2 | 3 | class EmptyDevice implements Device { 4 | @override 5 | int get channel => throw UnimplementedError('EmptyDevice'); 6 | 7 | @override 8 | bool get isEnabled => throw UnimplementedError('EmptyDevice'); 9 | 10 | @override 11 | int get volume => throw UnimplementedError('EmptyDevice'); 12 | 13 | @override 14 | void printStatus() { 15 | print('------------------------------------'); 16 | print('| Device is Empty'); 17 | print('------------------------------------\n'); 18 | } 19 | 20 | @override 21 | set channel(int channel) => throw UnimplementedError('EmptyDevice'); 22 | 23 | @override 24 | set isEnabled(bool enabled) => throw UnimplementedError('EmptyDevice'); 25 | 26 | @override 27 | set volume(int percent) => throw UnimplementedError('EmptyDevice'); 28 | } 29 | -------------------------------------------------------------------------------- /patterns/builder/conceptual/README.md: -------------------------------------------------------------------------------- 1 | # Builder Pattern 2 | Builder is a creational design pattern that lets you construct complex objects step by step. The 3 | pattern allows you to produce different types and representations of an object using the same 4 | construction code. 5 | 6 | ## Diagram: 7 | 8 | ![image](https://user-images.githubusercontent.com/8049534/182850365-52969fc7-d743-430b-acc7-da400eae26aa.png) 9 | 10 | ## Client code: 11 | 12 | ```dart 13 | void main() { 14 | final director = Director(); 15 | 16 | final product1 = director.construct(ConcreteBuilder1()); 17 | print(product1); 18 | 19 | final product2 = director.construct(ConcreteBuilder2()); 20 | print(product2); 21 | } 22 | ``` 23 | 24 | ### Output: 25 | 26 | ``` 27 | ConcreteBuilder1 28 | 001: one 29 | 002: two 30 | 003: three 31 | 32 | ConcreteBuilder2 33 | 1️⃣: first 34 | 2️⃣: second 35 | 3️⃣: third 36 | ``` 37 | -------------------------------------------------------------------------------- /patterns/iterator/github_commit/github/github_loader.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import '../pattern/github_repo.dart'; 4 | 5 | class GitHubLoader { 6 | static Future get({required userName, required repoName}) async { 7 | final url = Uri.http( 8 | 'api.github.com', 9 | 'repos/$userName/$repoName/commits', 10 | {'per_page': '10'}, 11 | ); 12 | final json = await _loadCommits(url); 13 | return GitHubRepo(json); 14 | } 15 | 16 | static Future> _loadCommits(Uri url) async { 17 | final client = HttpClient(); 18 | try { 19 | final response = await client.getUrl(url); 20 | final request = await response.close(); 21 | final content = await request.transform(utf8.decoder).join(); 22 | return jsonDecode(content); 23 | } finally { 24 | client.close(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/render_elements/render_row.dart: -------------------------------------------------------------------------------- 1 | import 'render_element.dart'; 2 | import 'render_layout.dart'; 3 | import 'render_position.dart'; 4 | 5 | class RenderRow extends RenderLayout { 6 | RenderRow({ 7 | required List children, 8 | int space = 3, 9 | }) : super(children: children, space: space); 10 | 11 | @override 12 | int get width => childWidth + spacesSum; 13 | 14 | @override 15 | int get height => 16 | children.reduce((a, b) => a.height > b.height ? a : b).height; 17 | 18 | @override 19 | List compute() { 20 | final result = []; 21 | var x = 0; 22 | final y = 0; 23 | 24 | for (final child in children) { 25 | result.add( 26 | RenderPosition(x: x, y: y, child: child), 27 | ); 28 | x += child.width + space; 29 | } 30 | 31 | return result; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /patterns/memento/conceptual/app/snapshot.dart: -------------------------------------------------------------------------------- 1 | import 'editor.dart'; 2 | 3 | /// EN: The memento class stores the past state of the editor. 4 | /// 5 | /// RU: Снимок хранит прошлое состояние редактора. 6 | class Snapshot { 7 | final Editor _editor; 8 | final String _text; 9 | final int _curX; 10 | final int _curY; 11 | final int _selectionWidth; 12 | 13 | Snapshot( 14 | this._editor, 15 | this._text, 16 | this._curX, 17 | this._curY, 18 | this._selectionWidth, 19 | ); 20 | 21 | /// EN: At some point, a previous state of the editor can be 22 | /// restored using a memento object. 23 | /// 24 | /// RU: В нужный момент владелец снимка может восстановить 25 | /// состояние редактора. 26 | void restore() { 27 | _editor 28 | ..text = _text 29 | ..curX = _curX 30 | ..curY = _curY 31 | ..selectionWidth = _selectionWidth; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /patterns/observer/app_observer/main.dart: -------------------------------------------------------------------------------- 1 | import 'observer/app_observer.dart'; 2 | import 'observer/event.dart'; 3 | 4 | class FirstEvent extends Event {} 5 | 6 | class SecondEvent extends Event {} 7 | 8 | class ThirdEvent extends Event {} 9 | 10 | void main() { 11 | final observer = AppObserver(); 12 | 13 | observer.subscribe((e) { 14 | print('First'); 15 | }); 16 | 17 | observer.subscribe((SecondEvent e) { 18 | print('Second'); 19 | }); 20 | 21 | final saveThirdEvent = observer.subscribe((ThirdEvent e) { 22 | print('Third'); 23 | }); 24 | 25 | observer.notify(FirstEvent()); 26 | observer.notify(SecondEvent()); 27 | observer.notify(ThirdEvent()); 28 | 29 | print('---unsubscribe "ThirdEvent"---'); 30 | observer.unsubscribe(saveThirdEvent); 31 | 32 | observer.notify(FirstEvent()); 33 | observer.notify(SecondEvent()); 34 | observer.notify(ThirdEvent()); 35 | } 36 | -------------------------------------------------------------------------------- /patterns/command/text_editor/commands/cut_command.dart: -------------------------------------------------------------------------------- 1 | part of copy_past; 2 | 3 | class CutCommand extends CopyCommand { 4 | int? _cursorPosition; 5 | 6 | CutCommand(Application app) : super(app); 7 | 8 | @override 9 | bool get isSaveHistory => _copyText.isNotEmpty; 10 | 11 | @override 12 | void execute() { 13 | if (!app.editor.cursor.isTextSelected) { 14 | return; 15 | } 16 | 17 | super.execute(); 18 | app.editor.removeSelected(); 19 | _cursorPosition = app.editor.cursor.position; 20 | } 21 | 22 | @override 23 | void undo() { 24 | if (_copyText.isEmpty) { 25 | return; 26 | } 27 | 28 | app.editor 29 | ..cursorPosition = _cursorPosition! 30 | ..inputText(_copyText); 31 | } 32 | 33 | @override 34 | String toString() { 35 | return 'Cut( ' 36 | 'cursorPosition: $_cursorPosition, ' 37 | 'cutText: "$_copyText" )'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/main.dart: -------------------------------------------------------------------------------- 1 | import 'products/box.dart'; 2 | import 'products/product.dart'; 3 | import 'products/product_leaf.dart'; 4 | 5 | void main() { 6 | final diagram = createParcel().toDiagram(); 7 | print(diagram.renderToText()); 8 | } 9 | 10 | Product createParcel() { 11 | return Box( 12 | children: [ 13 | Box.single( 14 | ProductLeaf('Hammer', 9), 15 | ), 16 | Box( 17 | children: [ 18 | Box( 19 | children: [ 20 | Box.single( 21 | ProductLeaf('Phone', 450), 22 | ), 23 | Box.single( 24 | ProductLeaf('Headphones', 30), 25 | ), 26 | ], 27 | ), 28 | Box.single( 29 | ProductLeaf('Charger', 25), 30 | ), 31 | ], 32 | ), 33 | ProductLeaf('Receipt', 0), 34 | ], 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'adapter/classic_app_adapter_widget.dart' as adapter; 4 | import 'client_app/app.dart'; 5 | import 'client_app/widgets/text_property_widget.dart'; 6 | 7 | class FlutterAdapterApp extends StatefulWidget { 8 | @override 9 | State createState() => _FlutterAdapterAppState(); 10 | } 11 | 12 | class _FlutterAdapterAppState extends State { 13 | final app = App(); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Scaffold( 18 | body: Column( 19 | children: [ 20 | TextPropertyWidget( 21 | classicApp: app, 22 | ), 23 | Expanded( 24 | child: adapter.ClassicAppAdapterWidget( 25 | classicApp: app, 26 | ), 27 | ), 28 | ], 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/pattern/manipulator.dart: -------------------------------------------------------------------------------- 1 | library manipulator; 2 | 3 | import 'package:flutter/material.dart'; 4 | import '../states/_/paint_style.dart'; 5 | import '../app/shapes.dart'; 6 | 7 | part 'manipulation_state.dart'; 8 | part '../app/base_manipulation.dart'; 9 | 10 | abstract class Manipulator { 11 | ManipulationState get state; 12 | 13 | Shapes get shapes; 14 | 15 | MouseCursor get cursor; 16 | 17 | set cursor(MouseCursor cursor); 18 | 19 | PaintStyle get paintStyle; 20 | 21 | Event get onStateChange; 22 | 23 | Event get onUpdate; 24 | 25 | void changeState(ManipulationState newState); 26 | 27 | void update(); 28 | 29 | void mouseMove(double x, double y); 30 | 31 | void mouseDown(double x, double y); 32 | 33 | void mouseUp(); 34 | 35 | void mouseDoubleClick(double x, double y); 36 | 37 | void keyDown(KeyEvent keyEvent); 38 | 39 | void paint(Canvas canvas); 40 | } 41 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/products/box.dart: -------------------------------------------------------------------------------- 1 | import '../diagram/diagram.dart'; 2 | import 'product.dart'; 3 | 4 | class Box implements Product { 5 | Box({required this.children}); 6 | 7 | final List children; 8 | 9 | @override 10 | String get content { 11 | final places = size > 1 ? "places: $size, " : ""; 12 | final localPrice = size > 1 ? "price: $price\$" : "$price\$"; 13 | return 'Box($places$localPrice)'; 14 | } 15 | 16 | @override 17 | Diagram toDiagram() { 18 | return Diagram.parentNode( 19 | this, 20 | children, 21 | ); 22 | } 23 | 24 | @override 25 | int get price => children.fold(0, (sum, product) => sum + product.price); 26 | 27 | @override 28 | int get size => children.fold(0, (sum, product) => sum + product.size); 29 | 30 | factory Box.single(Product productLeaf) { 31 | return Box( 32 | children: [productLeaf], 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/shapes/circle_shape.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'base_shape.dart'; 4 | 5 | class CircleShape extends BaseShape { 6 | final double radius; 7 | final bool isFilled; 8 | 9 | CircleShape({ 10 | required this.radius, 11 | required this.isFilled, 12 | required double x, 13 | required double y, 14 | required Color color, 15 | }) : super( 16 | x: x, 17 | y: y, 18 | color: color, 19 | ); 20 | 21 | @override 22 | void paint(Canvas can) { 23 | final pos = width / 2; 24 | can.drawCircle( 25 | Offset(pos, pos), 26 | radius - 2, 27 | Paint() 28 | ..style = isFilled ? PaintingStyle.fill : PaintingStyle.stroke 29 | ..color = color, 30 | ); 31 | } 32 | 33 | @override 34 | double get width => radius * 2; 35 | 36 | @override 37 | double get height => width; 38 | } 39 | -------------------------------------------------------------------------------- /patterns/proxy/conceptual/README.md: -------------------------------------------------------------------------------- 1 | # Proxy Pattern 2 | Proxy is a structural design pattern that lets you provide a substitute or placeholder for another 3 | object. A proxy controls access to the original object, allowing you to perform something either 4 | before or after the request gets through to the original object. 5 | 6 | Tutorial: [here](https://refactoring.guru/design-patterns/proxy). 7 | 8 | ### Diagram: 9 | ![image](https://user-images.githubusercontent.com/8049534/175926828-d4fed7c6-ea82-4717-a24b-8ad2b23910ba.png) 10 | 11 | ### Client code: 12 | ```dart 13 | void main() async { 14 | final subject = Proxy(); 15 | print(subject.request()); // print "Proxy data" 16 | 17 | print('Wait for 2 seconds...'); 18 | await Future.delayed(Duration(seconds: 2)); 19 | 20 | print(subject.request()); // print "Real data" 21 | } 22 | ``` 23 | 24 | ### Output: 25 | ``` 26 | Proxy data. 27 | Wait 2 seconds... 28 | Real data. 29 | ``` 30 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/main.dart: -------------------------------------------------------------------------------- 1 | import 'operations/and.dart'; 2 | import 'operations/or.dart'; 3 | import 'operations/xor.dart'; 4 | import 'pattern/context.dart'; 5 | import 'variable/bool_variable.dart'; 6 | 7 | void main() { 8 | final context = Context(); 9 | final variable1 = BoolVariable('var1'); 10 | final variable2 = BoolVariable('var2'); 11 | final variable3 = BoolVariable('var3'); 12 | final variable4 = BoolVariable('var4'); 13 | 14 | context.assign(variable1, true); 15 | context.assign(variable2, false); 16 | context.assign(variable3, true); 17 | context.assign(variable4, false); 18 | 19 | final expression = And( 20 | variable1, // true 21 | Xor( 22 | variable2, // false 23 | Or( 24 | variable3, // true 25 | variable4, // false 26 | ), 27 | ), 28 | ); 29 | 30 | print(expression.toDebugString(context)); 31 | print('result: ${expression.evaluate(context)}'); 32 | } 33 | -------------------------------------------------------------------------------- /patterns/template_method/data_miner/miners/zip_miner.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../pattern/data_miner.dart'; 4 | import '../utils/raw_data.dart'; 5 | 6 | class ZipMiner extends DataMiner { 7 | ZipMiner(super.fileName); 8 | 9 | @override 10 | RawData extractData(File file) { 11 | final content = File('${reportPath}refactoring_guru_workers.zip') 12 | .readAsBytesSync() 13 | .toList(); 14 | final unzip = GZipCodec().decode(content); 15 | return String.fromCharCodes(unzip); 16 | } 17 | 18 | @override 19 | StringTable parseData(RawData raw) { 20 | final lines = raw.split('\n'); 21 | final divIndex = lines.length ~/ 2; 22 | final names = lines.sublist(0, divIndex); 23 | final indexes = lines.sublist(divIndex); 24 | 25 | return [ 26 | for (var i = 0; i < divIndex; i++) 27 | [ 28 | names[i].trim(), 29 | indexes[i].trim(), 30 | ] 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /patterns/adapter/text_graphics/shapes/shape.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | abstract class Shape { 4 | final int x; 5 | final int y; 6 | final Color color; 7 | 8 | Shape(this.x, this.y, this.color); 9 | 10 | void draw(Canvas can); 11 | } 12 | 13 | class Rectangle extends Shape { 14 | final int width; 15 | final int height; 16 | 17 | Rectangle({ 18 | required int x, 19 | required int y, 20 | required this.width, 21 | required this.height, 22 | required Color color, 23 | }) : super(x, y, color); 24 | 25 | @override 26 | void draw(Canvas can) => can.rectangle(width, height); 27 | } 28 | 29 | class Circle extends Shape { 30 | final int radius; 31 | 32 | Circle({ 33 | required int x, 34 | required int y, 35 | required this.radius, 36 | required Color color, 37 | }) : super(x, y, color); 38 | 39 | @override 40 | void draw(Canvas can) => can.circle(radius); 41 | } 42 | -------------------------------------------------------------------------------- /patterns/builder/cars/builders/car_builder.dart: -------------------------------------------------------------------------------- 1 | import '../cars/car.dart'; 2 | import '../cars/car_type.dart'; 3 | import '../components/engine.dart'; 4 | import '../director/gps_navigation.dart'; 5 | import '../director/transmission.dart'; 6 | import '../director/trip_computer.dart'; 7 | import 'builder.dart'; 8 | 9 | /// EN: Concrete builders implement steps defined in the common interface. 10 | /// 11 | /// RU: Конкретные строители реализуют шаги, объявленные в общем интерфейсе. 12 | class CarBuilder implements Builder { 13 | @override 14 | CarType? carType; 15 | 16 | @override 17 | int? seats; 18 | 19 | @override 20 | Engine? engine; 21 | 22 | @override 23 | Transmission? transmission; 24 | 25 | @override 26 | TripComputer? tripComputer; 27 | 28 | @override 29 | GPSNavigator? gpsNavigator; 30 | 31 | Car getResult() { 32 | return Car( 33 | carType!, seats!, engine!, transmission!, tripComputer!, gpsNavigator!); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /patterns/command/text_editor/commands/input_command.dart: -------------------------------------------------------------------------------- 1 | import '../application/application.dart'; 2 | import 'command.dart'; 3 | 4 | class InputCommand extends Command { 5 | final String _addText; 6 | int? _startPosition; 7 | String _prevSelectText = ''; 8 | 9 | InputCommand(Application app, this._addText) : super(app); 10 | 11 | @override 12 | bool get isSaveHistory => _addText.isNotEmpty; 13 | 14 | @override 15 | void execute() { 16 | _prevSelectText = app.editor.selectedText; 17 | _startPosition = app.editor.cursor.startSelection; 18 | app.editor.inputText(_addText); 19 | } 20 | 21 | @override 22 | void undo() { 23 | app.editor 24 | ..selectText(_startPosition!, _startPosition! + _addText.length) 25 | ..inputText(_prevSelectText); 26 | } 27 | 28 | @override 29 | String toString() { 30 | return 'Input( ' 31 | 'cursorPosition: $_startPosition, ' 32 | 'addText: "$_addText" )'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/text/text_size_marker_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | import '../../../shapes/text_shape.dart'; 4 | import '../../_/marker.dart'; 5 | 6 | class TextSizeMarkerState extends Marker { 7 | TextSizeMarkerState({required super.parentState}); 8 | 9 | @override 10 | void mouseDragAction(double x, double y) { 11 | final newHeight = 12 | y - selectedShape.y - (selectedShape.height - selectedShape.userHeight); 13 | 14 | selectedShape.resize(0, newHeight); 15 | } 16 | 17 | @override 18 | void updatePosition() { 19 | final bottomCenter = selectedShape.rect.bottomCenter; 20 | markerShape.move(bottomCenter.dx, bottomCenter.dy); 21 | } 22 | 23 | @override 24 | MouseCursor get hoverCursor => SystemMouseCursors.resizeUpDown; 25 | 26 | @override 27 | String toString() { 28 | return '${parentState.toString()} + Text Size Marker State'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/client_app/business_rules/text_coloring.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../../classic_app/repaint_compatible.dart'; 4 | 5 | class TextColoring { 6 | final RepaintCompatible _repaintContext; 7 | 8 | TextColoring(this._repaintContext); 9 | 10 | final maxTextSize = 400; 11 | 12 | var _size = 50; 13 | 14 | int get size => _size; 15 | 16 | set size(int newVal) { 17 | if (newVal == _size) { 18 | return; 19 | } 20 | 21 | if (newVal > maxTextSize) { 22 | _size = maxTextSize; 23 | } else if (newVal < 1) { 24 | _size = 1; 25 | } else { 26 | _size = newVal; 27 | } 28 | 29 | _repaintContext.repaint(); 30 | } 31 | 32 | var _color = Color(0xffd81b60); 33 | 34 | Color get color => _color; 35 | 36 | set color(Color newColor) { 37 | if (_color == newColor) { 38 | return; 39 | } 40 | 41 | _color = newColor; 42 | _repaintContext.repaint(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /patterns/iterator/word_iterator/README.md: -------------------------------------------------------------------------------- 1 | # Iterator pattern 2 | Iterator is a behavioral design pattern that lets you traverse elements of a collection without 3 | exposing its underlying representation (list, stack, tree, etc.). 4 | 5 | Tutorial: [here](https://refactoring.guru/design-patterns/iterator). 6 | 7 | ### Client code: 8 | ```dart 9 | void main() { 10 | final text = Text( 11 | 'Iterator is a behavioral design pattern that lets you traverse elements ' 12 | 'of a collection without exposing its underlying representation ' 13 | '(list, stack, tree, etc.).', 14 | ); 15 | 16 | for (final s in text) { 17 | print(s); 18 | } 19 | } 20 | ``` 21 | 22 | **Output:** 23 | ``` 24 | Iterator 25 | is 26 | a 27 | behavioral 28 | design 29 | pattern 30 | that 31 | lets 32 | you 33 | traverse 34 | elements 35 | of 36 | a 37 | collection 38 | without 39 | exposing 40 | its 41 | underlying 42 | representation 43 | list 44 | stack 45 | tree 46 | etc 47 | ``` 48 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' 4 | as adapter; 5 | import 'application.dart'; 6 | import 'widgets/right_panel_widget.dart'; 7 | 8 | 9 | class FlutterMementoEditorApp extends StatefulWidget { 10 | @override 11 | State createState() => 12 | _FlutterMementoEditorAppState(); 13 | } 14 | 15 | class _FlutterMementoEditorAppState extends State { 16 | final app = MementoEditorApplication(); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | body: Row( 22 | children: [ 23 | Expanded( 24 | child: adapter.ClassicAppAdapterWidget( 25 | classicApp: app.editor, 26 | ), 27 | ), 28 | RightPanelWidget(app: app), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /patterns/visitor/conceptual/before/main.dart: -------------------------------------------------------------------------------- 1 | import 'elements/elements.dart'; 2 | 3 | void main() { 4 | final list = createElements(); 5 | 6 | list.forEach(operation1); 7 | list.forEach(operation2); 8 | } 9 | 10 | Iterable createElements() { 11 | return [ 12 | One(), 13 | Two(), 14 | Three(), 15 | ]; 16 | } 17 | 18 | void operation1(Object obj) { 19 | if (obj is One) { 20 | print('operation1: one (param1 = ${obj.param1})'); 21 | } else if (obj is Two) { 22 | print('operation1: two (param2 = ${obj.param2})'); 23 | } else if (obj is Three) { 24 | print('operation1: two (param3 = ${obj.param3})'); 25 | } 26 | } 27 | 28 | void operation2(Object obj) { 29 | if (obj is One) { 30 | print('operation2: one (param1 = ${obj.param1})'); 31 | } else if (obj is Two) { 32 | print('operation2: two (param2 = ${obj.param2})'); 33 | } else if (obj is Three) { 34 | print('operation2: two (param3 = ${obj.param3})'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /patterns/command/text_editor/commands/select_command.dart: -------------------------------------------------------------------------------- 1 | import '../application/application.dart'; 2 | import '../application/text_cursor.dart'; 3 | import 'command.dart'; 4 | 5 | class SelectCommand extends Command { 6 | final int _start; 7 | final int _end; 8 | 9 | TextCursor? _previousCursor; 10 | 11 | SelectCommand(Application app, this._start, this._end) : super(app); 12 | 13 | @override 14 | bool get isSaveHistory => false; 15 | 16 | @override 17 | void execute() { 18 | _previousCursor = app.editor.cursor; 19 | app.editor.selectText(_start, _end); 20 | } 21 | 22 | @override 23 | void undo() { 24 | if (_previousCursor == null) { 25 | return; 26 | } 27 | 28 | app.editor.selectText( 29 | _previousCursor!.startSelection, 30 | _previousCursor!.endSelection, 31 | ); 32 | } 33 | 34 | @override 35 | String toString() { 36 | return 'Select( ' 37 | 'start: $_start, ' 38 | 'end: $_end )'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /patterns/chain_of_responsibility/server_middleware/server/server.dart: -------------------------------------------------------------------------------- 1 | library server; 2 | 3 | part 'middleware.dart'; 4 | 5 | class Server { 6 | final Map _users; 7 | final Middleware middleware; 8 | 9 | Server({ 10 | required Map users, 11 | required this.middleware, 12 | }) : _users = Map.of(users) { 13 | _applyServerToAllMiddleware(); 14 | } 15 | 16 | bool logIn(String email, String password) { 17 | print('Sever: login User("$email", "$password")'); 18 | return middleware.check(email, password); 19 | } 20 | 21 | void _applyServerToAllMiddleware() { 22 | Middleware? middleware = this.middleware; 23 | while (middleware != null) { 24 | middleware._server = this; 25 | middleware = middleware.next; 26 | } 27 | } 28 | 29 | bool hasEmail(String email) => _users.containsKey(email); 30 | 31 | bool isValidPassword(String email, String password) { 32 | return _users[email] == password; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/render_elements/render_column.dart: -------------------------------------------------------------------------------- 1 | import 'render_element.dart'; 2 | import 'render_layout.dart'; 3 | import 'render_position.dart'; 4 | 5 | class RenderColumn extends RenderLayout { 6 | RenderColumn({ 7 | required List children, 8 | int space = 1, 9 | }) : super(children: children, space: space); 10 | 11 | @override 12 | int get width => children.reduce((a, b) => a.width > b.width ? a : b).width; 13 | 14 | @override 15 | int get height => childHeight + spacesSum; 16 | 17 | @override 18 | List compute() { 19 | final result = []; 20 | var y = 0; 21 | 22 | for (final child in children) { 23 | final xCenter = (width - child.width) ~/ 2; 24 | result.add( 25 | RenderPosition( 26 | x: xCenter, 27 | y: y, 28 | child: child, 29 | ), 30 | ); 31 | 32 | y += child.height + space; 33 | } 34 | return result; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/property_widgets/factories/property_widget_factories.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../../../pattern/property.dart'; 4 | 5 | import 'property_widget_factory.dart'; 6 | 7 | class PropertyWidgetFactories { 8 | final List _factories; 9 | 10 | PropertyWidgetFactories({ 11 | required List factories, 12 | }) : _factories = factories; 13 | 14 | Widget createPropertyWidgetFrom(Property property) { 15 | for (final factory in _factories) { 16 | if (factory.isPropertyCompatible(property)) { 17 | return factory.createWidget(property); 18 | } 19 | } 20 | 21 | throw 'Value(${property.value()}) property is not support.'; 22 | } 23 | 24 | Iterable createListWidgetsFrom( 25 | Iterable properties, 26 | ) sync* { 27 | for (final property in properties) { 28 | yield createPropertyWidgetFrom(property); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/selections/inner_radius_markers/inner_radius_marker_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/src/services/mouse_cursor.dart'; 2 | 3 | import '../../../shapes/circle_shape.dart'; 4 | import '../../_/marker.dart'; 5 | 6 | class InnerRadiusMarkerState extends Marker { 7 | InnerRadiusMarkerState({required super.parentState}); 8 | 9 | @override 10 | void mouseDragAction(double x, double y) { 11 | selectedShape.innerRadius = selectedShape.rect.right - x; 12 | } 13 | 14 | @override 15 | void updatePosition() { 16 | final y = selectedShape.y + selectedShape.height / 2; 17 | final x = selectedShape.x + selectedShape.width; 18 | 19 | markerShape.move( 20 | x - selectedShape.innerRadius, 21 | y, 22 | ); 23 | } 24 | 25 | @override 26 | MouseCursor get hoverCursor => SystemMouseCursors.resizeLeftRight; 27 | 28 | @override 29 | String toString() { 30 | return '${parentState.toString()} + Inner Radius Marker State'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /patterns/command/text_editor/main.dart: -------------------------------------------------------------------------------- 1 | import 'application/application.dart'; 2 | import 'commands/command.dart'; 3 | 4 | void main() { 5 | final app = Application(); 6 | app.addListenerExecuteCommand(log); 7 | app 8 | ..keyboardInput('Hello world') 9 | ..moveCursor(position: 6) 10 | ..keyboardInput('Refactring GuBu ') 11 | ..selectText(start: 17, end: 19) 12 | ..ctrlX() 13 | ..selectText(start: 17, end: 19) 14 | ..keyboardInput('Guru') 15 | ..selectText(start: 4, end: 5) 16 | ..ctrlC() 17 | ..moveCursor(position: 12) 18 | ..ctrlV() 19 | ..moveCursor(position: 28); 20 | 21 | print('\nUndo steps\n'); 22 | 23 | while (app.isHistoryNotEmpty) { 24 | app.ctrlZ(); 25 | } 26 | } 27 | 28 | void log({ 29 | required Command command, 30 | required bool isUndo, 31 | required String editorText, 32 | }) { 33 | final addOrUndo = isUndo ? 'Undo_' : '[➕] '; 34 | final description = '$addOrUndo$command'; 35 | print('${description.padRight(72, '_')}"$editorText"'); 36 | } 37 | -------------------------------------------------------------------------------- /patterns/composite/products_and_boxes/render_elements/render_layout.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:design_patterns_dart/text_canvas.dart'; 4 | 5 | import 'render_element.dart'; 6 | import 'render_position.dart'; 7 | 8 | abstract class RenderLayout extends RenderElement { 9 | RenderLayout({ 10 | required this.children, 11 | this.space = 3, 12 | }); 13 | 14 | late final List positions = compute(); 15 | 16 | List compute(); 17 | 18 | final List children; 19 | final int space; 20 | 21 | int get childWidth => children.fold( 22 | 0, (width, renderElement) => width + renderElement.width); 23 | 24 | int get childHeight => children.fold( 25 | 0, (height, renderElement) => height + renderElement.height); 26 | 27 | int get spacesSum => max(0, (children.length - 1) * space); 28 | 29 | @override 30 | void render(Canvas dc) { 31 | for (final child in positions) { 32 | child.render(dc); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/shapes/line_shape.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'base_shape.dart'; 6 | 7 | class LineShape extends BaseShape { 8 | final double length; 9 | 10 | LineShape({ 11 | required bool isMirror, 12 | required this.length, 13 | required double x, 14 | required double y, 15 | required Color color, 16 | }) : super( 17 | x: x, 18 | y: y, 19 | color: color, 20 | ) { 21 | if (isMirror) { 22 | point1 = Offset(0, length); 23 | point2 = Offset(length, 0); 24 | } else { 25 | point1 = Offset(0, 0); 26 | point2 = Offset(length, length); 27 | } 28 | } 29 | 30 | late final Offset point1; 31 | late final Offset point2; 32 | 33 | @override 34 | void paint(Canvas can) { 35 | can.drawLine(point1, point2, Paint()..color = color); 36 | } 37 | 38 | @override 39 | double get width => length; 40 | 41 | @override 42 | double get height => length; 43 | } 44 | -------------------------------------------------------------------------------- /patterns/memento/conceptual/app/editor.dart: -------------------------------------------------------------------------------- 1 | import 'snapshot.dart'; 2 | 3 | /// EN: The originator holds some important data that may change over 4 | /// time. It also defines a method for saving its state inside a 5 | /// memento and another method for restoring the state from it. 6 | /// 7 | /// RU: Класс создателя должен иметь специальный метод, который. 8 | /// сохраняет состояние создателя в новом объекте-снимке. 9 | class Editor { 10 | var text = ''; 11 | var curX = 0; 12 | var curY = 0; 13 | var selectionWidth = 0; 14 | 15 | Editor(this.text); 16 | 17 | /// EN: Saves the current state inside a memento. 18 | Snapshot createSnapshot() { 19 | /// EN: Memento is an immutable object; that's why the 20 | /// originator passes its state to the memento's 21 | /// constructor parameters. 22 | /// 23 | /// RU: Снимок — неизменяемый объект, поэтому Создатель 24 | /// передаёт все своё состояние через параметры 25 | /// конструктора. 26 | return Snapshot(this, text, curX, curY, selectionWidth); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /patterns/decorator/data_source_decoder/src/compression_decorator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'data_source.dart'; 5 | import 'data_source_decorator.dart'; 6 | 7 | class CompressionDecorator extends DataSourceDecorator { 8 | var compressionLevel = 6; 9 | 10 | CompressionDecorator(DataSource source) : super(source); 11 | 12 | @override 13 | void writeData(String data) { 14 | super.writeData(_compress(data)); 15 | } 16 | 17 | @override 18 | String readData() { 19 | return _decompress(super.readData()); 20 | } 21 | 22 | String _compress(String stringData) { 23 | final gzip = GZipCodec(level: compressionLevel); 24 | final encodeStr = base64.encode( 25 | gzip.encode(stringData.codeUnits), 26 | ); 27 | return encodeStr; 28 | } 29 | 30 | String _decompress(String stringData) { 31 | final gzip = GZipCodec(level: compressionLevel); 32 | final decodeStr = gzip.decode( 33 | base64.decode(stringData), 34 | ); 35 | return String.fromCharCodes(decodeStr); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/client_app/widgets/slider_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SliderWidget extends StatelessWidget { 4 | final int current; 5 | final int max; 6 | final void Function(int newSize) onChange; 7 | 8 | const SliderWidget({ 9 | Key? key, 10 | required this.current, 11 | required this.max, 12 | required this.onChange, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Row( 18 | children: [ 19 | SizedBox( 20 | width: 31, 21 | child: Text( 22 | '$current', 23 | textAlign: TextAlign.right, 24 | ), 25 | ), 26 | SizedBox( 27 | width: 200, 28 | child: Slider( 29 | value: current.toDouble(), 30 | max: max.toDouble(), 31 | min: 1, 32 | onChanged: (newVal) { 33 | onChange(newVal.toInt()); 34 | }, 35 | ), 36 | ), 37 | ], 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_launcher", 3 | "short_name": "flutter_launcher", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/shapes/base_shape.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'shape.dart'; 4 | 5 | abstract class BaseShape implements Shape { 6 | BaseShape(double x, double y, double width, double height) 7 | : _x = x, 8 | _y = y, 9 | _width = width, 10 | _height = height; 11 | 12 | @override 13 | double get x => _x; 14 | 15 | @override 16 | double get y => _y; 17 | 18 | @override 19 | double get height => _height; 20 | 21 | @override 22 | double get width => _width; 23 | 24 | @override 25 | Rect get rect => Rect.fromLTWH( 26 | _width < 0 ? x + _width : x, 27 | _height < 0 ? y + _height : y, 28 | _width.abs(), 29 | _height.abs(), 30 | ); 31 | 32 | @override 33 | void move(double x, double y) { 34 | _x = x; 35 | _y = y; 36 | } 37 | 38 | @override 39 | void resize(double width, double height) { 40 | _width = width; 41 | _height = height; 42 | } 43 | 44 | double _x; 45 | double _y; 46 | double _width; 47 | double _height; 48 | } 49 | -------------------------------------------------------------------------------- /patterns/bridge/devices_remote_control/devices/tv.dart: -------------------------------------------------------------------------------- 1 | import 'device.dart'; 2 | 3 | class Tv implements Device { 4 | bool _on = false; 5 | int _volume = 30; 6 | int _channel = 1; 7 | 8 | @override 9 | bool get isEnabled => _on; 10 | 11 | @override 12 | set isEnabled(bool enabled) => _on = enabled; 13 | 14 | @override 15 | int get volume => _volume; 16 | 17 | @override 18 | set volume(int percent) { 19 | if (volume > 100) { 20 | _volume = 100; 21 | } else if (volume < 0) { 22 | _volume = 0; 23 | } else { 24 | _volume = volume; 25 | } 26 | } 27 | 28 | @override 29 | int get channel => _channel; 30 | 31 | @override 32 | set channel(int channel) => _channel = channel; 33 | 34 | @override 35 | void printStatus() { 36 | print('------------------------------------'); 37 | print('| I\'m TV set.'); 38 | print('| I\'m ${_on ? 'enabled' : 'disabled'}'); 39 | print('| Current volume is $_volume%'); 40 | print('| Current channel is $_channel'); 41 | print('------------------------------------\n'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patterns/bridge/devices_remote_control/devices/radio.dart: -------------------------------------------------------------------------------- 1 | import 'device.dart'; 2 | 3 | class Radio implements Device { 4 | bool _on = false; 5 | int _volume = 30; 6 | int _channel = 1; 7 | 8 | @override 9 | bool get isEnabled => _on; 10 | 11 | @override 12 | set isEnabled(bool enabled) => _on = enabled; 13 | 14 | @override 15 | int get volume => _volume; 16 | 17 | @override 18 | set volume(int percent) { 19 | if (volume > 100) { 20 | _volume = 100; 21 | } else if (volume < 0) { 22 | _volume = 0; 23 | } else { 24 | _volume = volume; 25 | } 26 | } 27 | 28 | @override 29 | int get channel => _channel; 30 | 31 | @override 32 | set channel(int channel) => _channel = channel; 33 | 34 | @override 35 | void printStatus() { 36 | print('------------------------------------'); 37 | print('| I\'m radio.'); 38 | print('| I\'m ${_on ? 'enabled' : 'disabled'}'); 39 | print('| Current volume is $_volume%'); 40 | print('| Current channel is $_channel'); 41 | print('------------------------------------\n'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patterns/mediator/conceptual/concrete_mediator/concrete_mediator.dart: -------------------------------------------------------------------------------- 1 | import '../components/component1.dart'; 2 | import '../components/component2.dart'; 3 | import '../pattern/mediator.dart'; 4 | 5 | class ConcreteMediator extends Mediator { 6 | final Component1 component1; 7 | final Component2 component2; 8 | 9 | ConcreteMediator(this.component1, this.component2) { 10 | applyThisMediator(component1); 11 | applyThisMediator(component2); 12 | } 13 | 14 | @override 15 | void notify(Component component, String event) { 16 | print('ConcreteMediator.notify(event: "$event")'); 17 | 18 | if (component == component1) { 19 | reactComponentOne(); 20 | } else if (component == component2) { 21 | reactComponentTwo(); 22 | } 23 | } 24 | 25 | void reactComponentOne() { 26 | print('ConcreteMediator.reactComponentOne()'); 27 | print('use component2.name = "${component2.name}"'); 28 | } 29 | 30 | void reactComponentTwo() { 31 | print('ConcreteMediator.reactComponentTwo()'); 32 | print('use component1.sate = "${component1.sate}"'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /patterns/observer/open_close_editor_events/event_manager/event_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../listeners/event_listener.dart'; 4 | 5 | class EventManager { 6 | final _listeners = >{}; 7 | 8 | EventManager(List operations) { 9 | for (final operation in operations) { 10 | _listeners[operation] = []; 11 | } 12 | } 13 | 14 | void subscribe(String eventType, EventListener listener) { 15 | _usersBy(eventType).add(listener); 16 | } 17 | 18 | void unsubscribe(String eventType, EventListener listener) { 19 | _usersBy(eventType).remove(listener); 20 | } 21 | 22 | void notify(String eventType, File file) { 23 | final users = _usersBy(eventType); 24 | for (final listener in users) { 25 | listener.update(eventType, file); 26 | } 27 | } 28 | 29 | List _usersBy(String eventType) { 30 | final users = _listeners[eventType]; 31 | 32 | if (users == null) { 33 | throw UnsupportedError('Event type "$eventType" do not support.'); 34 | } 35 | 36 | return users; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/factories/line_factory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../mixin/icon_box_mixin.dart'; 4 | import '../pattern/property.dart'; 5 | import '../pattern/tool_factory.dart'; 6 | import '../pattern/shape.dart'; 7 | import '../shapes/line_shape.dart'; 8 | 9 | class LineFactory extends ToolFactory with IconBoxMixin { 10 | var _isMirror = true; 11 | var _length = 100.0; 12 | 13 | @override 14 | Shape createShape(double x, double y, Color color) { 15 | return LineShape( 16 | length: _length, 17 | isMirror: _isMirror, 18 | x: x, 19 | y: y, 20 | color: color, 21 | ); 22 | } 23 | 24 | @override 25 | Iterable get properties { 26 | return [ 27 | Property( 28 | name: 'mirror', 29 | value: () => _isMirror, 30 | onChange: (val) { 31 | _isMirror = val; 32 | }, 33 | ), 34 | Property( 35 | name: 'length', 36 | value: () => _length, 37 | onChange: (val) { 38 | _length = val; 39 | }, 40 | ), 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/factories/star_factory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../mixin/icon_box_mixin.dart'; 4 | import '../pattern/property.dart'; 5 | import '../pattern/tool_factory.dart'; 6 | import '../pattern/shape.dart'; 7 | import '../shapes/star_shape.dart'; 8 | 9 | class StarFactory extends ToolFactory with IconBoxMixin { 10 | var _radius = 80.0; 11 | var _isFilled = false; 12 | 13 | @override 14 | Shape createShape(double x, double y, Color color) { 15 | return StarShape( 16 | radius: _radius, 17 | isFilled: _isFilled, 18 | x: x, 19 | y: y, 20 | color: color, 21 | ); 22 | } 23 | 24 | @override 25 | Iterable get properties { 26 | return [ 27 | Property( 28 | name: 'radius', 29 | value: () => _radius, 30 | onChange: (val) { 31 | _radius = val; 32 | }, 33 | ), 34 | Property( 35 | name: 'filled', 36 | value: () => _isFilled, 37 | onChange: (val) { 38 | _isFilled = val; 39 | }, 40 | ), 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/factories/text_factory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../mixin/icon_box_mixin.dart'; 4 | import '../pattern/property.dart'; 5 | import '../pattern/tool_factory.dart'; 6 | import '../shapes/text_shape.dart'; 7 | import '../pattern/shape.dart'; 8 | 9 | class TextFactory extends ToolFactory with IconBoxMixin { 10 | var _text = 'Text'; 11 | var _fontSize = 50.0; 12 | 13 | @override 14 | Shape createShape(double x, double y, Color color) { 15 | return TextShape( 16 | text: _text, 17 | fontSize: _fontSize, 18 | x: x, 19 | y: y, 20 | color: color, 21 | ); 22 | } 23 | 24 | @override 25 | Iterable get properties { 26 | return [ 27 | Property( 28 | name: 'text', 29 | value: () => _text, 30 | onChange: (value) { 31 | _text = value; 32 | }, 33 | ), 34 | Property( 35 | name: 'fontSize', 36 | value: () => _fontSize, 37 | onChange: (value) { 38 | _fontSize = value; 39 | }, 40 | ), 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patterns/facade/conceptual/README.md: -------------------------------------------------------------------------------- 1 | # Facade pattern 2 | Facade is a structural design pattern that provides a simplified interface to a library, a 3 | framework, or any other complex set of classes. 4 | 5 | Tutorial: [here](https://refactoring.guru/design-patterns/facade). 6 | 7 | ### About example. 8 | This the very conceptual example rewrite from original source code [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/facade/example) 9 | 10 | ### Diagram: 11 | ![image](https://user-images.githubusercontent.com/8049534/183629745-a62d81be-f640-48e8-b70c-00d6cf59aa5f.png) 12 | 13 | ### Client code: 14 | ```dart 15 | void main() { 16 | final converter = VideoConversionFacade(); 17 | final mp4Video = converter.convertVideo("youtubevideo.ogg", "mp4"); 18 | // ...; 19 | } 20 | ``` 21 | 22 | ### Output: 23 | ``` 24 | VideoConversionFacade: conversion started. 25 | CodecFactory: extracting ogg audio... 26 | BitrateReader: reading file... 27 | BitrateReader: writing file... 28 | AudioMixer: fixing audio... 29 | VideoConversionFacade: conversion completed. 30 | ``` 31 | -------------------------------------------------------------------------------- /patterns/bridge/clock/README.md: -------------------------------------------------------------------------------- 1 | # Bridge pattern 2 | Bridge is a structural design pattern that lets you split a large class or a set of closely related 3 | classes into two separate hierarchies—abstraction and implementation—which can be developed independently of each other. 4 | 5 | ## Example 6 | The idea for the bridge pattern example is taken from [here](https://habr.com/ru/post/85137/). 7 | 8 | ### Diagram: 9 | ![image](https://user-images.githubusercontent.com/8049534/145851578-f6e95355-e2b3-4f94-bda2-c2d1d0de8935.png) 10 | 11 | ### Client code: 12 | ```dart 13 | void main() { 14 | final melody = Melody(); 15 | final signal = Signal(); 16 | startClocks([ 17 | Once(seconds: 1, bell: melody), 18 | Once(seconds: 2, bell: signal), 19 | Interval(seconds: 5, bell: melody), 20 | Interval(seconds: 3, bell: signal), 21 | ]); 22 | } 23 | 24 | void startClocks(List clocks) { 25 | for (final clock in clocks) { 26 | clock.start(); 27 | } 28 | } 29 | ``` 30 | 31 | **Output:** 32 | 33 | https://user-images.githubusercontent.com/8049534/145850512-27a5e9ea-4f76-4d52-b784-9bc88aee4de8.mp4 34 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/factories/circle_factory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../mixin/icon_box_mixin.dart'; 4 | import '../pattern/property.dart'; 5 | import '../pattern/tool_factory.dart'; 6 | import '../shapes/circle_shape.dart'; 7 | import '../pattern/shape.dart'; 8 | 9 | class CircleFactory extends ToolFactory with IconBoxMixin { 10 | var _radius = 50.0; 11 | var _isFilled = false; 12 | 13 | @override 14 | Shape createShape(double x, double y, Color color) { 15 | return CircleShape( 16 | radius: _radius, 17 | isFilled: _isFilled, 18 | x: x, 19 | y: y, 20 | color: color, 21 | ); 22 | } 23 | 24 | @override 25 | Iterable get properties { 26 | return [ 27 | Property( 28 | name: 'radius', 29 | value: () => _radius, 30 | onChange: (val) { 31 | _radius = val; 32 | }, 33 | ), 34 | Property( 35 | name: 'filled', 36 | value: () => _isFilled, 37 | onChange: (val) { 38 | _isFilled = val; 39 | }, 40 | ), 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/colors_tool_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../app/tools.dart'; 4 | import 'independent/tool_bar.dart'; 5 | import 'independent/tool_button.dart'; 6 | 7 | class ColorsToolBar extends StatelessWidget { 8 | final Tools tools; 9 | 10 | const ColorsToolBar({Key? key, required this.tools}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return ToolBar( 15 | title: 'colors', 16 | child: ValueListenableBuilder( 17 | valueListenable: tools.activeColor, 18 | builder: (_, activeColor, __) { 19 | return Column( 20 | children: [ 21 | for (final color in tools.colors) 22 | ToolButton( 23 | icon: Icon(Icons.circle, color: color), 24 | active: color == activeColor, 25 | onTap: () { 26 | tools.activeColor.value = color; 27 | }, 28 | ), 29 | ], 30 | ); 31 | }, 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /patterns/mediator/conceptual/README.md: -------------------------------------------------------------------------------- 1 | # Mediator Pattern 2 | Mediator is a behavioral design pattern that lets you reduce chaotic dependencies between objects. 3 | The pattern restricts direct communications between the objects and forces them to collaborate only 4 | via a mediator object. 5 | 6 | Tutorial: [here](https://refactoring.guru/design-patterns/mediator). 7 | 8 | ## Conceptual diagram: 9 | ![image](https://user-images.githubusercontent.com/8049534/173237874-971dd4e7-2e74-4cac-bcea-77b88255adad.png) 10 | 11 | ### Client code: 12 | ```dart 13 | void main() { 14 | final component1 = Component1(); 15 | final component2 = Component2(); 16 | 17 | ConcreteMediator(component1, component2); 18 | 19 | component1.doOne(); 20 | print(''); 21 | component2.doTwo(); 22 | } 23 | ``` 24 | 25 | ### Output: 26 | ``` 27 | call Component1.doOne() 28 | ConcreteMediator.notify(event: "doOne") 29 | ConcreteMediator.reactComponentOne() 30 | use component2.name = "Two" 31 | 32 | call Component2.doTwo() 33 | ConcreteMediator.notify(event: "doTwo") 34 | ConcreteMediator.reactComponentTwo() 35 | use component1.sate = "Cmp1" 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/independent/panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Panel extends StatelessWidget { 4 | static const thicknessWidth = 64.0; 5 | final double thicknessHeight; 6 | 7 | final Axis direction; 8 | final Widget child; 9 | 10 | const Panel({ 11 | Key? key, 12 | required this.direction, 13 | required this.child, 14 | this.thicknessHeight = 48.0, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Container( 20 | height: direction == Axis.horizontal ? thicknessHeight : null, 21 | width: direction == Axis.vertical ? thicknessWidth : null, 22 | clipBehavior: Clip.antiAlias, 23 | decoration: BoxDecoration( 24 | color: Color(0xFF3B3B3B), 25 | borderRadius: BorderRadius.all(Radius.circular(3)), 26 | boxShadow: [ 27 | BoxShadow( 28 | color: Color(0x7C000000), 29 | blurRadius: 6, 30 | offset: Offset(2, 2), 31 | ), 32 | ], 33 | ), 34 | child: child, 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'hove.dart'; 4 | 5 | class ToolButton extends StatelessWidget { 6 | final Function() onTap; 7 | final bool active; 8 | final Widget icon; 9 | 10 | const ToolButton({ 11 | Key? key, 12 | required this.onTap, 13 | this.active = false, 14 | required this.icon, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return GestureDetector( 20 | onTap: onTap, 21 | child: Hover( 22 | builder: (hoverForce) { 23 | return Container( 24 | width: 64, 25 | height: 64, 26 | color: color(hoverForce), 27 | child: Padding( 28 | padding: EdgeInsets.all(16), 29 | child: icon, 30 | ), 31 | ); 32 | }, 33 | ), 34 | ); 35 | } 36 | 37 | Color color(double hoverForce) => active 38 | ? Color.lerp(Colors.white10, Colors.white12, hoverForce)! 39 | : Color.lerp(Colors.transparent, Colors.white10, hoverForce)!; 40 | } 41 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/app/tools.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | 6 | import '../mixin/icon_box_mixin.dart'; 7 | import '../pattern/tool_factory.dart'; 8 | 9 | class Tools { 10 | final List factories; 11 | final List colors; 12 | 13 | late final ValueNotifier activeFactory; 14 | 15 | late final ValueNotifier activeColor; 16 | 17 | Future get iconsReady => _iconsInitCompleter.future; 18 | 19 | Tools({required this.factories, required this.colors}) 20 | : assert(factories.isNotEmpty), 21 | assert(colors.isNotEmpty) { 22 | activeFactory = ValueNotifier(factories.first); 23 | activeColor = ValueNotifier(colors.first); 24 | _initIconsFromShapes(); 25 | } 26 | 27 | final _iconsInitCompleter = Completer(); 28 | 29 | void _initIconsFromShapes() async { 30 | await Future.wait([ 31 | for (final factory in factories) 32 | (factory as IconBoxMixin).updateIcon(activeColor.value), 33 | ]); 34 | _iconsInitCompleter.complete(Future.value(true)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/factories/triangle_factory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import '../mixin/icon_box_mixin.dart'; 4 | import '../pattern/property.dart'; 5 | import '../pattern/tool_factory.dart'; 6 | import '../pattern/shape.dart'; 7 | import '../shapes/triangle_shape.dart'; 8 | 9 | class TriangleFactory extends ToolFactory with IconBoxMixin { 10 | var _isFilled = false; 11 | var _sideLength = 120.0; 12 | 13 | @override 14 | Shape createShape(double x, double y, Color color) { 15 | return TriangleShape( 16 | sideLength: _sideLength, 17 | isFilled: _isFilled, 18 | x: x, 19 | y: y, 20 | color: color, 21 | ); 22 | } 23 | 24 | @override 25 | Iterable get properties => [ 26 | Property( 27 | name: 'sideLength', 28 | value: () => _sideLength, 29 | onChange: (val) { 30 | _sideLength = val; 31 | }, 32 | ), 33 | Property( 34 | name: 'filled', 35 | value: () => _isFilled, 36 | onChange: (val) { 37 | _isFilled = val; 38 | }, 39 | ), 40 | ]; 41 | } 42 | -------------------------------------------------------------------------------- /patterns/command/text_editor/commands/past_command.dart: -------------------------------------------------------------------------------- 1 | import '../application/application.dart'; 2 | import 'command.dart'; 3 | 4 | class PastCommand extends Command { 5 | String _text = ''; 6 | String _selectText = ''; 7 | int? _cursorPosition; 8 | 9 | PastCommand(Application app) : super(app); 10 | 11 | @override 12 | bool get isSaveHistory => _text.isNotEmpty; 13 | 14 | @override 15 | void execute() { 16 | if (app.clipboard.isEmpty) { 17 | return; 18 | } 19 | 20 | _selectText = app.editor.selectedText; 21 | _text = app.clipboard; 22 | app.editor.inputText(_text); 23 | _cursorPosition = app.editor.cursor.position; 24 | } 25 | 26 | @override 27 | void undo() { 28 | if (_text.isEmpty) { 29 | return; 30 | } 31 | 32 | app.editor 33 | ..selectText( 34 | _cursorPosition! - _text.length, 35 | _cursorPosition!, 36 | ) 37 | ..inputText(_selectText); 38 | } 39 | 40 | @override 41 | String toString() { 42 | return 'Past( ' 43 | 'cursorPosition: $_cursorPosition, ' 44 | 'text: "$_text", ' 45 | 'pevRestore: "$_selectText" )'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'dialogs_factory/dialog.dart'; 4 | import 'dialogs_factory/html_dialog.dart'; 5 | import 'dialogs_factory/windows_dialog.dart'; 6 | 7 | late Dialog dialog; 8 | 9 | void main() { 10 | configure(); 11 | runBusinessLogic(); 12 | } 13 | 14 | /// EN: The concrete factory is usually chosen depending on configuration or 15 | /// environment options. 16 | /// 17 | /// RU: Приложение создаёт определённую фабрику в зависимости от конфигурации 18 | /// или окружения. 19 | void configure() { 20 | if (Platform.isWindows) { 21 | dialog = WindowsDialog(); 22 | } else { 23 | dialog = HtmlDialog(); 24 | } 25 | } 26 | 27 | /// EN: All of the client code should work with factories and products 28 | /// through abstract interfaces. This way it does not care which factory it 29 | /// works with and what kind of product it returns. 30 | /// 31 | /// RU: Весь остальной клиентский код работает с фабрикой и продуктами только 32 | /// через общий интерфейс, поэтому для него неважно какая фабрика была 33 | /// создана. 34 | void runBusinessLogic() { 35 | dialog.renderWindow(); 36 | } 37 | -------------------------------------------------------------------------------- /patterns/iterator/word_iterator/pattern/word_iterator.dart: -------------------------------------------------------------------------------- 1 | import '../text/text.dart'; 2 | 3 | class WordIterator extends Iterator { 4 | WordIterator(this._text); 5 | 6 | @override 7 | String get current => _currWord!; 8 | 9 | @override 10 | bool moveNext() { 11 | final start = _lastIndex; 12 | 13 | while (_searchNextSpaceChar(' ')) { 14 | // ++ 15 | } 16 | 17 | _currWord = _getWord(start, _lastIndex); 18 | return _currWord!.isNotEmpty; 19 | } 20 | 21 | final Text _text; 22 | int _lastIndex = 0; 23 | String? _currWord; 24 | 25 | bool _searchNextSpaceChar(String char) { 26 | final isTextEnd = _lastIndex >= _text.text.length; 27 | 28 | if (isTextEnd) { 29 | return false; 30 | } 31 | 32 | final isNotSpaceChar = _text.text[_lastIndex++] != char; 33 | return isNotSpaceChar; 34 | } 35 | 36 | String _getWord(int start, int end) { 37 | final noWordChars = RegExp(r'\W'); 38 | return _text.text 39 | .substring( 40 | start, 41 | _lastIndex, 42 | ) 43 | .replaceAll( 44 | noWordChars, 45 | '', 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/shapes/shape.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_final_fields 2 | 3 | import 'dart:ui'; 4 | 5 | part 'active_shape.dart'; 6 | 7 | class Shape { 8 | double _x; 9 | 10 | double get x => _x; 11 | 12 | double _y; 13 | 14 | double get y => _y; 15 | 16 | Color _color; 17 | 18 | Color get color => _color; 19 | 20 | double _size; 21 | 22 | double get size => _size; 23 | 24 | Shape( 25 | this._x, 26 | this._y, [ 27 | this._color = const Color(0xFFFFFFFF), 28 | this._size = 60.0, 29 | ]); 30 | 31 | static final _paintStroke = Paint() 32 | ..style = PaintingStyle.stroke 33 | ..color = Color(0xFFD81B60) 34 | ..strokeWidth = 2; 35 | 36 | void paint(Canvas canvas) { 37 | final paintFill = Paint() 38 | ..style = PaintingStyle.fill 39 | ..color = color; 40 | 41 | final offset = Offset(x, y); 42 | canvas.drawCircle(offset, _size, paintFill); 43 | canvas.drawCircle(offset, _size, _paintStroke); 44 | } 45 | 46 | bool isBounded(double x, double y) { 47 | return ((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) <= 48 | _size * _size); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /patterns/observer/subscriber_flutter_widget/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import '../app_observer/observer/app_observer.dart'; 5 | import 'events/new_hash_event.dart'; 6 | import 'widgets/hash_generator_widget.dart'; 7 | import 'widgets/hash_user_widget.dart'; 8 | 9 | class SubscriberFlutterApp extends StatefulWidget { 10 | @override 11 | State createState() => _SubscriberFlutterAppState(); 12 | } 13 | 14 | class _SubscriberFlutterAppState extends State { 15 | final observer = AppObserver(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | body: Center( 21 | child: Column( 22 | mainAxisAlignment: MainAxisAlignment.center, 23 | children: [ 24 | HashUserWidget(observer: observer), 25 | HashGeneratorWidget(onHashGenerate: onHashGenerate), 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | 32 | void onHashGenerate() { 33 | final hash = Random().nextDouble().hashCode.toString(); 34 | observer.notify(NewHashEvent(hash)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/_/mixins/hover_shape_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../pattern/manipulator.dart'; 4 | import '../../../shapes/shape.dart'; 5 | 6 | mixin HoverShapeMixin implements ManipulationState { 7 | Shape? findShapeByCoordinates(double x, double y) { 8 | return context.shapes.findShapeByCoordinates(x, y); 9 | } 10 | 11 | Shape? get hoverShape => _hoverShape; 12 | 13 | bool get isHover => _hoverShape != null; 14 | 15 | @override 16 | void mouseMove(double x, double y) { 17 | final newHover = findShapeByCoordinates(x, y); 18 | 19 | if (newHover == _hoverShape) { 20 | return; 21 | } 22 | 23 | _hoverShape = newHover; 24 | 25 | if (newHover == null) { 26 | onMouseLeave(); 27 | } else { 28 | onHover(); 29 | } 30 | 31 | context.update(); 32 | } 33 | 34 | void onHover() {} 35 | 36 | void onMouseLeave() {} 37 | 38 | @override 39 | void paint(Canvas canvas) { 40 | if (_hoverShape == null) { 41 | return; 42 | } 43 | 44 | context.paintStyle.paintHover(canvas, _hoverShape!); 45 | } 46 | 47 | Shape? _hoverShape; 48 | } 49 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | library_private_types_in_public_api: false 21 | # analyzer: 22 | # exclude: 23 | # - path/to/excluded/files/** 24 | 25 | # For more information about the core and recommended set of lints, see 26 | # https://dart.dev/go/core-lints 27 | 28 | # For additional information about configuring this file, see 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/independent/tool_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'panel.dart'; 4 | 5 | class ToolBar extends StatelessWidget { 6 | final String title; 7 | final Widget child; 8 | 9 | const ToolBar({ 10 | Key? key, 11 | required this.title, 12 | required this.child, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Panel( 18 | direction: Axis.vertical, 19 | child: Column( 20 | children: [ 21 | _buildTitle(title), 22 | child, 23 | ], 24 | ), 25 | ); 26 | } 27 | 28 | Widget _buildTitle(String title) { 29 | return Container( 30 | height: 20, 31 | color: Colors.white10, 32 | width: double.infinity, 33 | alignment: Alignment.center, 34 | child: Text( 35 | title, 36 | style: TextStyle( 37 | color: Colors.white70, 38 | fontSize: 13, 39 | fontFamily: 'Arial', 40 | decoration: TextDecoration.none, 41 | fontWeight: FontWeight.normal, 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /patterns/composite/image_editor/shapes/base_shape.dart: -------------------------------------------------------------------------------- 1 | import 'package:design_patterns_dart/text_canvas.dart'; 2 | 3 | import '../editor/image_editor.dart'; 4 | import 'shape.dart'; 5 | 6 | abstract class BaseShape implements Shape { 7 | int _x; 8 | int _y; 9 | final Color color; 10 | 11 | bool _selected = false; 12 | 13 | BaseShape(this._x, this._y, this.color); 14 | 15 | @override 16 | int get x => _x; 17 | 18 | @override 19 | int get y => _y; 20 | 21 | @override 22 | void move(int x, int y) { 23 | _x += x; 24 | _y += y; 25 | } 26 | 27 | @override 28 | void select() => _selected = true; 29 | 30 | @override 31 | void unSelect() => _selected = false; 32 | 33 | @override 34 | bool get isSelected => _selected; 35 | 36 | void enableSelectionStyle(Graphics graphics) { 37 | graphics.penColor = Color.white; 38 | } 39 | 40 | void disableSelectionStyle(Graphics graphics) { 41 | graphics.penColor = color; 42 | } 43 | 44 | @override 45 | void paint(Graphics graphics) { 46 | if (isSelected) { 47 | enableSelectionStyle(graphics); 48 | } else { 49 | disableSelectionStyle(graphics); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/shapes/triangle_shape.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'base_shape.dart'; 7 | 8 | class TriangleShape extends BaseShape { 9 | final double sideLength; 10 | final bool isFilled; 11 | 12 | TriangleShape({ 13 | required this.sideLength, 14 | required this.isFilled, 15 | required double x, 16 | required double y, 17 | required Color color, 18 | }) : super(x: x, y: y, color: color) { 19 | _trianglePath = Path() 20 | ..addPolygon( 21 | [ 22 | Offset(0, height), 23 | Offset(width / 2, 0), 24 | Offset(width, height), 25 | ], 26 | true, 27 | ); 28 | } 29 | 30 | late final Path _trianglePath; 31 | 32 | @override 33 | void paint(Canvas can) { 34 | can.drawPath( 35 | _trianglePath, 36 | Paint() 37 | ..style = isFilled ? PaintingStyle.fill : PaintingStyle.stroke 38 | ..color = color); 39 | } 40 | 41 | @override 42 | double get width => sideLength; 43 | 44 | @override 45 | double get height => sideLength * sqrt(3) / 2; 46 | } 47 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/independent/event_listenable_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class EventListenableBuilder extends StatefulWidget { 4 | final T event; 5 | final Widget Function(BuildContext context) builder; 6 | 7 | const EventListenableBuilder({ 8 | Key? key, 9 | required this.event, 10 | required this.builder, 11 | }) : super(key: key); 12 | 13 | @override 14 | _EventListenableBuilderState createState() => 15 | _EventListenableBuilderState(event); 16 | } 17 | 18 | class _EventListenableBuilderState 19 | extends State> { 20 | final T event; 21 | 22 | _EventListenableBuilderState(this.event); 23 | 24 | @override 25 | void initState() { 26 | event.addListener(_update); 27 | super.initState(); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | event.removeListener(_update); 33 | super.dispose(); 34 | } 35 | 36 | void _update() { 37 | setState(() {}); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return widget.builder(context); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /patterns/abstract_factory/conceptual_gui_factory/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Factory pattern 2 | Abstract Factory is a creational design pattern that lets you produce families of related objects 3 | without specifying their concrete classes. 4 | 5 | Tutorial: [here](https://refactoring.guru/design-patterns/abstract-factory). 6 | 7 | ### About example. 8 | This the very conceptual example rewrite from original source code [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/abstract_factory/example) 9 | 10 | ### Diagram: 11 | ![image](https://user-images.githubusercontent.com/8049534/165987890-e64db9a3-4865-411c-a5c0-16da21043159.png) 12 | 13 | ### Client code: 14 | ```dart 15 | void main() { 16 | final guiFactory = GUIFactory(); 17 | final app = Application(guiFactory); 18 | app.paint(); 19 | } 20 | 21 | abstract class GUIFactory { 22 | factory GUIFactory() { 23 | if (Platform.isMacOS) { 24 | return MacOSFactory(); 25 | } else { 26 | return WindowsFactory(); 27 | } 28 | } 29 | 30 | /*...*/ 31 | } 32 | ``` 33 | 34 | ### Output: 35 | ``` 36 | You have created WindowsButton. 37 | You have created WindowsCheckbox. 38 | ``` 39 | -------------------------------------------------------------------------------- /patterns/factory_method/conceptual_platform_dialog/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Factory pattern 2 | Factory Method is a creational design pattern that provides an interface for creating objects in a 3 | superclass, but allows subclasses to alter the type of objects that will be created. 4 | 5 | Tutorial: [here](https://refactoring.guru/design-patterns/factory-method). 6 | 7 | ### About example. 8 | This the very conceptual example rewrite from original source code [java example](https://github.com/RefactoringGuru/design-patterns-java/tree/main/src/refactoring_guru/factory_method/example) 9 | 10 | ### Diagram: 11 | ![image](https://user-images.githubusercontent.com/8049534/166105090-a2b490fe-3e3e-44f1-a781-9777023020fb.png) 12 | 13 | ### Client code: 14 | ```dart 15 | late Dialog dialog; 16 | 17 | void main() { 18 | configure(); 19 | runBusinessLogic(); 20 | } 21 | 22 | void configure() { 23 | if (Platform.isWindows) { 24 | dialog = WindowsDialog(); 25 | } else { 26 | dialog = HtmlDialog(); 27 | } 28 | } 29 | 30 | void runBusinessLogic() { 31 | dialog.renderWindow(); 32 | } 33 | ``` 34 | 35 | ### Output: 36 | ``` 37 | Windows Button 38 | Click! Button says - "Hello World!" 39 | ``` 40 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/widgets/right_panel_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../application.dart'; 4 | import 'panels/shape_properties_widget.dart'; 5 | import 'panels/memento_widget.dart'; 6 | 7 | class RightPanelWidget extends StatelessWidget { 8 | final MementoEditorApplication app; 9 | 10 | RightPanelWidget({Key? key, required this.app}) : super(key: key); 11 | 12 | final colors = [ 13 | Color(0xFF000000), 14 | Color(0xFFD81B60), 15 | Color(0xFF5E35B1), 16 | Color(0xFF1E88E5), 17 | Color(0xFF43A047), 18 | Color(0xFFFFFFFF), 19 | ]; 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Container( 24 | padding: EdgeInsets.all(20), 25 | width: 300, 26 | child: Column( 27 | children: [ 28 | ShapePropertiesWidget(app: app, colors: colors), 29 | Container( 30 | margin: EdgeInsets.symmetric(vertical: 20), 31 | height: 2, 32 | color: Colors.black.withOpacity(.2), 33 | ), 34 | Expanded( 35 | child: MementoWidget(app: app), 36 | ), 37 | ], 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /patterns/state/manipulator_state/states/_/marker.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/rendering.dart'; 2 | 3 | import '../../shapes/marker_shape.dart'; 4 | import '../../shapes/shape.dart'; 5 | import 'sub_states/child_state.dart'; 6 | import 'sub_states/parent_state.dart'; 7 | 8 | abstract class Marker extends ChildState { 9 | Marker({ 10 | required ParentState parentState, 11 | }) : super( 12 | parentState: parentState, 13 | markerShape: MarkerShape(5), 14 | ); 15 | 16 | @override 17 | MouseCursor get hoverCursor { 18 | final rect = parentState.selectedShape.rect; 19 | final corner = Offset(markerShape.x, markerShape.y); 20 | 21 | if (corner == rect.topLeft) { 22 | return SystemMouseCursors.resizeUpLeft; 23 | } else if (corner == rect.topRight) { 24 | return SystemMouseCursors.resizeUpRight; 25 | } else if (corner == rect.bottomLeft) { 26 | return SystemMouseCursors.resizeDownLeft; 27 | } else if (corner == rect.bottomRight) { 28 | return SystemMouseCursors.resizeDownRight; 29 | } 30 | 31 | return SystemMouseCursors.move; 32 | } 33 | 34 | T get selectedShape => parentState.selectedShape; 35 | } 36 | -------------------------------------------------------------------------------- /patterns/interpreter/conceptual/README.md: -------------------------------------------------------------------------------- 1 | # Interpreter Pattern 2 | In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate 3 | sentences in a language. 4 | 5 | ## Diagram: 6 | ![Interpreter Diagram](https://user-images.githubusercontent.com/8049534/176169636-4c8eb3ba-d5e8-4ecb-81a8-96f1a30f6339.png) 7 | 8 | ## Client code: 9 | ```dart 10 | void main() { 11 | final context = Context(); 12 | final variable1 = BoolVariable('var1'); 13 | final variable2 = BoolVariable('var2'); 14 | final variable3 = BoolVariable('var3'); 15 | final variable4 = BoolVariable('var4'); 16 | 17 | context.assign(variable1, true); 18 | context.assign(variable2, false); 19 | context.assign(variable3, true); 20 | context.assign(variable4, false); 21 | 22 | final expression = And( 23 | variable1, // true 24 | Xor( 25 | variable2, // false 26 | Or( 27 | variable3, // true 28 | variable4, // false 29 | ), 30 | ), 31 | ); 32 | 33 | print(expression.evaluate(context)); 34 | print(expression.toDebugString(context)); 35 | } 36 | ``` 37 | 38 | ### Output: 39 | ``` 40 | var4(false) Or var3(true) Xor var2(false) And var1(true) 41 | result: true 42 | ``` 43 | -------------------------------------------------------------------------------- /patterns/memento/memento_editor/application.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'editor/memento_create_event.dart'; 4 | import 'editor/editor.dart'; 5 | import 'memento_pattern/caretaker.dart'; 6 | import 'memento_pattern/memento.dart'; 7 | import 'shapes/shape.dart'; 8 | 9 | class MementoEditorApplication { 10 | final editor = Editor(); 11 | final caretaker = Caretaker(); 12 | 13 | MementoEditorApplication() { 14 | createDefaultShapes(); 15 | } 16 | 17 | void createDefaultShapes() { 18 | const radius = 300.0; 19 | for (var i = 0; i < 7; i++) { 20 | final x = 60 + radius + cos(i / 1.15) * radius; 21 | final y = 60 + radius + sin(i / 1.15) * radius; 22 | editor.shapes.add(Shape(x, y)); 23 | } 24 | } 25 | 26 | void saveState() { 27 | final snapshot = editor.backup(); 28 | 29 | if (caretaker.isSnapshotExists(snapshot)) { 30 | return; 31 | } 32 | 33 | final memento = Memento(DateTime.now(), snapshot); 34 | caretaker.addMemento(memento); 35 | editor.events.notify(MementoCreateEvent()); 36 | } 37 | 38 | void restoreState(Memento memento) { 39 | editor 40 | ..unSelect() 41 | ..restore(memento.snapshot) 42 | ..repaint(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /patterns/abstract_factory/tool_panel_factory/widgets/independent/hove.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Hover extends StatefulWidget { 4 | final Function(double hoverForce) builder; 5 | 6 | const Hover({Key? key, required this.builder}) : super(key: key); 7 | 8 | @override 9 | _HoverState createState() => _HoverState(); 10 | } 11 | 12 | class _HoverState extends State with SingleTickerProviderStateMixin { 13 | late final AnimationController _animation; 14 | 15 | @override 16 | void initState() { 17 | _animation = AnimationController( 18 | duration: Duration(milliseconds: 200), 19 | value: 0.0, 20 | vsync: this, 21 | )..addListener( 22 | () { 23 | setState(() {}); 24 | }, 25 | ); 26 | super.initState(); 27 | } 28 | 29 | @override 30 | void dispose() { 31 | _animation.dispose(); 32 | super.dispose(); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return MouseRegion( 38 | onEnter: (_) { 39 | _animation.forward(from: _animation.value); 40 | }, 41 | onExit: (_) { 42 | _animation.reverse(); 43 | }, 44 | child: widget.builder(_animation.value), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /patterns/adapter/square_round_conflict/README.md: -------------------------------------------------------------------------------- 1 | # Adapter pattern 2 | Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate. 3 | 4 | ## Square and Round conflict example. 5 | **Description:** https://refactoring.guru/design-patterns/adapter?#pseudocode 6 | 7 | ### Class Diagram: 8 | ![example](https://user-images.githubusercontent.com/8049534/147594536-66627fa1-f4eb-42ba-b648-8757f9e5bf20.png) 9 | 10 | ### Client code: 11 | ```dart 12 | void main() { 13 | final hole = RoundHole(5); 14 | final peg = RoundPeg(5); 15 | if (hole.fits(peg)) { 16 | print("Round peg r5 fits round hole r5."); 17 | } 18 | 19 | final smallSqPeg = SquarePeg(2); 20 | final largeSqPeg = SquarePeg(20); 21 | 22 | final smallSqPegAdapter = SquarePegAdapter(smallSqPeg); 23 | final largeSqPegAdapter = SquarePegAdapter(largeSqPeg); 24 | 25 | if (hole.fits(smallSqPegAdapter)) { 26 | print("Square peg w2 fits round hole r5."); 27 | } 28 | if (!hole.fits(largeSqPegAdapter)) { 29 | print("Square peg w20 does not fit into round hole r5."); 30 | } 31 | } 32 | ``` 33 | 34 | **Output:** 35 | ``` 36 | Round peg r5 fits round hole r5. 37 | Square peg w2 fits round hole r5. 38 | Square peg w20 does not fit into round hole r5. 39 | ``` 40 | -------------------------------------------------------------------------------- /patterns/observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../../app_observer/observer/app_observer.dart'; 4 | import '../../app_observer/observer/event.dart'; 5 | 6 | class SubscriberWidget extends StatefulWidget { 7 | final AppObserver observer; 8 | final Widget Function(BuildContext buildContext, T? event) builder; 9 | 10 | const SubscriberWidget({ 11 | Key? key, 12 | required this.builder, 13 | required this.observer, 14 | }) : super(key: key); 15 | 16 | @override 17 | State> createState() { 18 | return _SubscriberWidgetState(); 19 | } 20 | } 21 | 22 | class _SubscriberWidgetState 23 | extends State> { 24 | T? _event; 25 | 26 | @override 27 | void initState() { 28 | widget.observer.subscribe(update); 29 | super.initState(); 30 | } 31 | 32 | @override 33 | void dispose() { 34 | widget.observer.unsubscribe(update); 35 | super.dispose(); 36 | } 37 | 38 | void update(T event) { 39 | setState(() => _event = event); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return widget.builder(context, _event); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /patterns/adapter/flutter_adapter/client_app/widgets/color_buttons_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ColorButtonsWidget extends StatelessWidget { 4 | final Color currentColor; 5 | final List colors; 6 | final void Function(Color color) onColorSelect; 7 | 8 | const ColorButtonsWidget({ 9 | Key? key, 10 | required this.currentColor, 11 | required this.colors, 12 | required this.onColorSelect, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Row( 18 | children: colors.map(_buildColorButton).toList(), 19 | ); 20 | } 21 | 22 | Widget _buildColorButton(Color color) { 23 | final isColorSelect = (color == currentColor); 24 | return GestureDetector( 25 | onTap: () { 26 | onColorSelect(color); 27 | }, 28 | child: Container( 29 | width: 20, 30 | height: 20, 31 | color: color, 32 | child: isColorSelect ? _buildSelectColorIcon() : null, 33 | ), 34 | ); 35 | } 36 | 37 | Widget _buildSelectColorIcon() { 38 | return Center( 39 | child: Container( 40 | width: 4, 41 | height: 4, 42 | color: Colors.white.withOpacity(0.8), 43 | ), 44 | ); 45 | } 46 | } 47 | --------------------------------------------------------------------------------