Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
dcc22d0
Add "ManipulatorStateApp" empty project.
ilopX May 22, 2022
2777d62
Add toolbar.
ilopX May 23, 2022
e99cc4d
Migrate to flutter 3.
ilopX May 23, 2022
3e11584
Add material icons.
ilopX May 23, 2022
1db9e2f
Add states.
ilopX May 23, 2022
50e10a5
Add "mouseDown" event to DrawingBoard.
ilopX May 23, 2022
1f44a84
Add empty behaviors states.
ilopX May 23, 2022
163eff9
Add default shape painting.
ilopX May 23, 2022
1b437b1
Make context is private.
ilopX May 23, 2022
60b0785
Fix: add type to "y" argument.
ilopX May 23, 2022
f77c5e5
Add x,y args to CreationState
ilopX May 23, 2022
fc48e34
Fix: x,y in mouseDown.
ilopX May 23, 2022
4a707b4
Draggable adds.
ilopX May 24, 2022
005c1d1
Add specific actions to states.
ilopX May 24, 2022
b508da3
Remove duplicate selection.
ilopX May 24, 2022
e5d9525
Add hover event.
ilopX May 24, 2022
ec59bed
Add HoverStateMixin.
ilopX May 24, 2022
23ac8cd
Move states mixin to separate directory.
ilopX May 24, 2022
edf5506
Add ResizableState.
ilopX May 24, 2022
13294bf
Add CircleShape.
ilopX May 24, 2022
dcbad12
Render Circle manipulation state.
ilopX May 24, 2022
3344d3d
Rename ManipulatorContext to ManipulationContext.
ilopX May 25, 2022
b711376
Add paint method to RectangleShape.
ilopX May 25, 2022
343cd90
Refactoring CreationState class.
ilopX May 25, 2022
53f67b6
Add sub "toString" to selection states.
ilopX May 25, 2022
2f39598
Add child MarkerState to ResizableState.
ilopX May 25, 2022
bad4384
Make left to right state text.
ilopX May 25, 2022
b518934
Fix: markers reposition.
ilopX May 25, 2022
2d04245
Add mouse effect to marker.
ilopX May 25, 2022
d76e900
Add ParentState & ChildState.
ilopX May 26, 2022
b45ad4b
New separate interface for ManipulationState.
ilopX May 26, 2022
65be1f4
Add corner markers for ResizableState.
ilopX May 26, 2022
ab80bfc
Add 'rect' property ru Shape.
ilopX May 26, 2022
104aa14
Rename ManipulationContext to Manipulator.
ilopX May 26, 2022
bfbca6a
Rename HoverStateMixin to HoverShape.
ilopX May 26, 2022
6abe572
Rename HoverStateMixin to HoverShapeMixin.
ilopX May 26, 2022
0e579fc
Add PaintStyle.
ilopX May 27, 2022
7670a5e
Fix cursor in corner markers.
ilopX May 27, 2022
f348d14
Fix cursor in corner markers.
ilopX May 27, 2022
8d58f62
Rename mouseMoveAction to mouseDragAction.
ilopX May 27, 2022
064e904
Fic CircleShape rect.
ilopX May 27, 2022
e969fba
Reversed shape search.
ilopX May 27, 2022
7625e79
Move changeState to children.
ilopX May 27, 2022
13ffcc9
Refactor CreationState.
ilopX May 27, 2022
f893476
Fix ToolBar: selected FreeState after creation shape.
ilopX May 27, 2022
92ce5c6
Add InnerRadiusState.
ilopX May 29, 2022
d6029ae
Rename trySelectAndStartMovingShape to tryToSelectAndStartMovingShape.
ilopX May 29, 2022
bc8e6d1
Add default width CircleCreationState.
ilopX May 29, 2022
8b93caf
Rename again tryToSelectAndStartMovingShape.
ilopX May 29, 2022
f1b21fa
Change SelectionState to ParentState int ChildState.
ilopX May 29, 2022
f8f1f18
Move resizable_state.dart to root directory.
ilopX May 29, 2022
f9e571c
Format tool_bar.dart.
ilopX May 29, 2022
8f364ad
Add generic type to SelectionState.selectedShape.
ilopX May 29, 2022
706d031
Move inner radius painter to PaintStyle.
ilopX May 29, 2022
5fcd818
InnerRadiusMarkerState add toString.
ilopX May 29, 2022
6bc1913
Add generic shape type to ChildState & rename shape to markerShape.
ilopX May 29, 2022
7c8d2a9
PaintStyle: move private vars to bottom.
ilopX May 29, 2022
268773e
Set radius line to 1.5.
ilopX May 29, 2022
28a9202
Refactor: create separate variable currentState.
ilopX May 29, 2022
7ebba53
Refactor: create separate function "buildButton".
ilopX May 29, 2022
4c3ed7b
Refactor: CurrentState widget.
ilopX May 29, 2022
ea96361
Add "paintSelection" method to PaintStyle.
ilopX May 29, 2022
e5993d6
Add selectedShape getter to CornerMarker.
ilopX May 29, 2022
bbfd133
Refactoring: RectangleShape & CircleShape.
ilopX May 30, 2022
b0ac3d1
Add TextShape, TextResizeState.
ilopX May 30, 2022
a1bc1a3
Fix wrong repeat selection in "tryToSelectAndStartMovingShape" function.
ilopX May 30, 2022
b4fcf68
Move the state helper classes to the "_" directory.
ilopX May 30, 2022
4a92407
Rename marker to child.
ilopX May 30, 2022
e657d95
Move abstract CreationState to "_" directory.
ilopX May 30, 2022
f7ba8e2
Fix TopRightMarkerState resize.
ilopX May 30, 2022
76ce7db
Refactoring InnerRadiusMarkerState. Replace selectedCircle variable t…
ilopX May 30, 2022
86d9533
Move TextSizeMarkerState to separate file.
ilopX May 30, 2022
fa13316
Refactoring BottomLeftMarkerState, TopRightMarkerState: remove unnece…
ilopX May 30, 2022
518ada6
Add double click event.
ilopX May 31, 2022
a909614
Add keyboard event.
ilopX May 31, 2022
8c478b8
Add TextChangeState.
ilopX May 31, 2022
399f86a
Add (x,y) to mousedDoubleClick.
ilopX Jun 1, 2022
95e370d
Add method init() to State interface.
ilopX Jun 1, 2022
f7c6fba
Fix height changer.
ilopX Jun 1, 2022
4d67df9
Add (x, y) to view.
ilopX Jun 1, 2022
7588a76
Add text editor.
ilopX Jun 1, 2022
0a01034
Move TextCursor to text_cursor.dart.
ilopX Jun 1, 2022
9507a28
Remove unnecessary vars.
ilopX Jun 1, 2022
484f69c
Add keyboard cursor moving: left, right.
ilopX Jun 2, 2022
385cf61
Add KeyboardActions & TextCursorAnimation.
ilopX Jun 2, 2022
23888d0
Refactoring TextCursor.
ilopX Jun 2, 2022
09fbf0e
Rename TextChangeState to TextEditState.
ilopX Jun 2, 2022
c42aadf
Refactoring: compare to class diagram.
ilopX Jun 2, 2022
2dde104
Add README.
ilopX Jun 2, 2022
1def8ee
Update main README.
ilopX Jun 2, 2022
8f95300
Bump version 0.24.0.
ilopX Jun 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.24.0
- Add state pattern: State Manipulator.

## 0.23.14
- Replace web renderer html to canvakit (deploy_flutter_demo.dart).

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ It contains **Dart** examples for all classic **GoF** design patterns.
- [ ] **Mediator**
- [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/memento/conceptual)] [[Memento Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/memento/memento_editor)]
- [x] **Observer** - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/open_close_editor_events)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/observer/subscriber_flutter_widget)]
- [ ] **State**
- [x] **State** - [[State Manipulator](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/state/manipulator_state)]
- [ ] **Template Method**
- [X] **Visitor** [[Shape XML Exporter](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/visitor/shapes_exporter)]
- [X] **Strategy** [[Reservation Cargo Spaces](https://github.com/RefactoringGuru/design-patterns-dart/tree/main/patterns/strategy/reservation_cargo_spaces)]
Expand All @@ -32,8 +32,8 @@ It contains **Dart** examples for all classic **GoF** design patterns.

## Requirements

The examples were written in **Dart 2.15**.
Some complex examples require **Flutter 2.12**.
The examples were written in **Dart 2.17**.
Some complex examples require **Flutter 3.0.0**.

## Contributor's Guide

Expand Down
7 changes: 3 additions & 4 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ include: package:lints/recommended.yaml

# Uncomment the following section to specify additional rules.

# linter:
# rules:
# - camel_case_types

linter:
rules:
library_private_types_in_public_api: false
# analyzer:
# exclude:
# - path/to/excluded/files/**
Expand Down
6 changes: 3 additions & 3 deletions bin/deploy_flutter_demos.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ void main() async {
clear();
}

late final tmpDir = Directory.systemTemp.createTempSync();
late final projectDir = thisPath(r'..\');
late final webBuildDir = Directory(projectDir.uri.toFilePath() + r'build\web');
final tmpDir = Directory.systemTemp.createTempSync();
final projectDir = thisPath(r'..\');
final webBuildDir = Directory(projectDir.uri.toFilePath() + r'build\web');
late final String originUrl;

Future<void> init() async {
Expand Down
12 changes: 10 additions & 2 deletions bin/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import '../patterns/abstract_factory/tool_panel_factory/main.dart';
import '../patterns/observer/subscriber_flutter_widget/main.dart';
import '../patterns/adapter/flutter_adapter/main.dart';
import '../patterns/memento/memento_editor/main.dart';
import '../patterns/state/manipulator_state/main.dart';

void main() {
runApp(MyApp());
Expand All @@ -13,13 +14,20 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Refactoring Guru: Flutter launcher',
theme: ThemeData(primarySwatch: Colors.pink),
initialRoute: '/abstract_factory/tool_panel_factory',
theme: ThemeData(
primarySwatch: Colors.pink,
iconTheme: IconThemeData(
size: 32,
color: Colors.white,
),
),
initialRoute: '/state/manipulator_state',
routes: {
'/observer/subscriber_flutter_widget': (_) => SubscriberFlutterApp(),
'/adapter/flutter_adapter': (_) => FlutterAdapterApp(),
'/memento/flutter_memento_editor': (_) => FlutterMementoEditorApp(),
'/abstract_factory/tool_panel_factory': (_) => ToolPanelFactoryApp(),
'/state/manipulator_state': (_) => ManipulatorStateApp(),
},
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:flutter/material.dart';

class Panel extends StatelessWidget {
static const thicknessWidth = 64.0;
static const thicknessHeight = 48.0;
final double thicknessHeight;

final Axis direction;
final Widget child;
Expand All @@ -11,6 +11,7 @@ class Panel extends StatelessWidget {
Key? key,
required this.direction,
required this.child,
this.thicknessHeight = 48.0,
}) : super(key: key);

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ToolButton extends StatelessWidget {
const ToolButton({
Key? key,
required this.onTap,
required this.active,
this.active = false,
required this.icon,
}) : super(key: key);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class FieldLabel extends StatelessWidget {
return Row(
children: [
SizedBox(width: 10),
Text(text + ':'),
Text('$text:'),
SizedBox(width: 10),
child,
SizedBox(width: 20),
Expand Down
2 changes: 1 addition & 1 deletion patterns/builder/cars/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ void main() {
// RU: Директор может знать больше одного рецепта строительства
director.constructSportsCar(manualBuilder);
final carManual = manualBuilder.getResult();
print("Car manual built:\n" + carManual.print());
print("Car manual built:\n${carManual.print()}");
}
2 changes: 1 addition & 1 deletion patterns/command/text_editor/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ void log({
}) {
final addOrUndo = isUndo ? 'Undo_' : '[➕] ';
final description = '$addOrUndo$command';
print(description.padRight(72, '_') + '"$editorText"');
print('${description.padRight(72, '_')}"$editorText"');
}
4 changes: 2 additions & 2 deletions patterns/composite/products_and_boxes/products/box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class Box implements Product {
@override
String get content {
final places = size > 1 ? "places: $size, " : "";
final _price = size > 1 ? "price: $price\$" : "$price\$";
return 'Box($places$_price)';
final localPrice = size > 1 ? "price: $price\$" : "$price\$";
return 'Box($places$localPrice)';
}

@override
Expand Down
76 changes: 76 additions & 0 deletions patterns/state/manipulator_state/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# State Pattern
State is a behavioral design pattern that lets an object alter its behavior when its internal state
changes. It appears as if the object changed its class.

Tutorial: [here](https://refactoring.guru/design-patterns/state).

### Online demo:
Click on the picture to see the [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/state/manipulator_state).

[![image](https://user-images.githubusercontent.com/8049534/171070341-1decb58f-033b-4eb5-89d4-355aafa6b680.png)](https://refactoringguru.github.io/design-patterns-dart/#/state/manipulator_state)

### Video
https://user-images.githubusercontent.com/8049534/171499203-1400c3ae-d5cd-4e48-a0b6-0252f4345d19.mp4

### Diagram:
![image](https://user-images.githubusercontent.com/8049534/171740942-659d3ec9-8355-4078-a7d6-b4a338b41187.png)

## Client code:
### Change FreeState to MoveState:
```dart
class FreeState extends ManipulationState {
@override
void mouseDown(double x, double y) {
tryToSelectAndStartMovingShape(x, y);
}

bool tryToSelectAndStartMovingShape(double x, double y) {
final selectedShape = context.shapes.findShapeByCoordinates(x, y);

context.changeState(
MoveState(
startX: x,
startY: y,
selectedShape: selectedShape,
),
);

return true;
}
}
```

### Change MoveState to ResizableState:
```dart
class MoveState extends SelectionState {
@override
void mouseMove(double x, double y) {
selectedShape.move(x, y);
context.update();
}

@override
void mouseUp() {
context.changeState(
selectedShape.createSelectionState(),
);
}
}
```

### Each shape has its own state manipulator:
```dart
class RectangleShape extends BaseShape {
@override
SelectionState createSelectionState() {
return ResizableState(selectedShape: this);
}
}

class CircleShape extends BaseShape {
@override
SelectionState createSelectionState() {
return InnerRadiusState(selectedShape: this);
}
}
```
15 changes: 15 additions & 0 deletions patterns/state/manipulator_state/app/app.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import '../pattern/manipulator.dart';
import 'shapes.dart';
import 'tool.dart';

class App {
final Shapes shapes;
final Manipulator manipulator;
final List<Tool> tools;

App({
required this.shapes,
required this.manipulator,
required this.tools,
});
}
87 changes: 87 additions & 0 deletions patterns/state/manipulator_state/app/base_manipulation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
part of manipulator;

class BaseManipulator implements Manipulator {
BaseManipulator({
required this.shapes,
required ManipulationState initState,
required this.paintStyle,
}) : _state = initState {
_state._context = this;
}

@override
ManipulationState get state => _state;

@override
final Shapes shapes;

@override
final onStateChange = Event();

@override
final onUpdate = Event();

@override
var cursor = MouseCursor.defer;

@override
final PaintStyle paintStyle;

@override
void changeState(ManipulationState newState) {
if (_state == newState) {
return;
}

_state = newState;
_state._context = this;
_state.init();
onStateChange._emit();
}

@override
void update() {
onUpdate._emit();
}

@override
void mouseMove(double x, double y) {
_state.mouseMove(x, y);
}

@override
void mouseDown(double x, double y) {
_state.mouseDown(x, y);
}

@override
void mouseUp() {
_state.mouseUp();
}

@override
void mouseDoubleClick(double x, double y) {
_state.mouseDoubleClick(x, y);
}

@override
void keyDown(KeyEvent keyEvent) {
_state.keyDown(keyEvent);
}

@override
void paint(Canvas canvas) {
_state.paint(canvas);
}

@override
String toString() {
return _state.toString();
}

ManipulationState _state;
}

class Event extends ChangeNotifier {
void _emit() => notifyListeners();
}
36 changes: 36 additions & 0 deletions patterns/state/manipulator_state/app/shapes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'dart:collection';
import 'dart:ui';

import 'package:flutter/foundation.dart';

import '../shapes/shape.dart';

class Shapes with IterableMixin<Shape> {
final List<Shape> _shapes;

Shapes(List<Shape> shapes) : _shapes = shapes;

void add(Shape shape) {
_shapes.add(shape);
onChange._emit();
}

@override
Iterator<Shape> get iterator => _shapes.iterator;

final onChange = Event();

Shape? findShapeByCoordinates(x, y) {
for (final shape in _shapes.reversed) {
if (shape.rect.contains(Offset(x, y))) {
return shape;
}
}

return null;
}
}

class Event extends ChangeNotifier {
void _emit() => notifyListeners();
}
13 changes: 13 additions & 0 deletions patterns/state/manipulator_state/app/tool.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:flutter/widgets.dart';

import '../pattern/manipulator.dart';

class Tool {
final Icon icon;
final ManipulationState state;

Tool({
required this.icon,
required this.state,
});
}
Loading