Majid Hajian - FlutterEngineering.io Author
FlutterEngineering.io
FlutterEngineering.io
www.flutterengineering.io
www.flutterengineering.io
GDE and Author
flutterdesignpatterns.com
Design Pattern Chapter Reviewer
Mangirdas Kazlauskas
FlutterEngineering.io
dealing with object creation
Such as Singleton, Factory method, prototype
focusing on class or object composition;
Such as Facade, Decorator, Adapter, Composite
which is about object interaction and responsibility;
Such as Command, Template method, State, Memento, Observer
FlutterEngineering.io
✅ Decoding the Role of Design Patterns
Factory Method Using Default Methods
Decorator by Chain Decorators
Execute Around Method Pattern
Strategy Pattern and Lightweight Strategies
Creating a Closed Hierarchy with Sealed
Summary
FlutterEngineering.io
FlutterEngineering.io
FlutterEngineering.io
Factory Method
FlutterEngineering.io
Factory Method
abstract class PlatformWidgetFactory {
Widget createWidget();
}
FlutterEngineering.io
Factory Method
class IOSWidgetFactory extends PlatformWidgetFactory {
@override
Widget createWidget() {
return CupertinoButton(
child: Text('iOS Button'),
onPressed: () {},
);
}
}
class AndroidWidgetFactory extends PlatformWidgetFactory {
@override
Widget createWidget() {
return ElevatedButton(
child: Text('Android Button'),
onPressed: () {},
);
}
}
class WebWidgetFactory extends PlatformWidgetFactory {
@override
Widget createWidget() {
return TextButton(
child: Text('Web Button'),
onPressed: () {},
);
}
}
FlutterEngineering.io
Factory Method
void main() {
PlatformWidgetFactory widgetFactory;
if (Platform.isIOS) {
widgetFactory = IOSWidgetFactory();
} else if (Platform.isAndroid) {
widgetFactory = AndroidWidgetFactory();
} else {
widgetFactory = WebWidgetFactory();
}
Widget myWidget = widgetFactory.createWidget();
runApp(MyApp(home: Scaffold(body: Center(child: myWidget))));
}
FlutterEngineering.io
Factory Method with Default Method
abstract class WidgetCreator {
// Factory method to be implemented by subclasses
Widget createWidget(BuildContext context);
// Core business logic that uses the factory method
Widget build(BuildContext context) {
final widget = createWidget(context);
return Scaffold(
appBar: AppBar(
title: Text("Factory Method Example"),
),
body: Center(child: widget),
);
}
}
FlutterEngineering.io
Factory Method with Default Method
abstract class CustomDialog {
// Factory method to be implemented by subclasses
Widget create(BuildContext context);
// Core business logic that uses the factory method
Future<void> show(BuildContext context) => showDialog<void>(
context: context,
barrierDismissible: false,
builder: create,
);
}
FlutterEngineering.io
Factory Method with Default Method
class AndroidAlertDialog extends CustomDialog {
@override
Widget create(BuildContext context) {
return AlertDialog(
title: Text('ANdroid'),
content: const Text('This is the material-style!'),
actions: <Widget>[
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Close'),
),
],
);
}
}
FlutterEngineering.io
Factory Method with Default Method
class IosAlertDialog extends CustomDialog {
@override
Widget create(BuildContext context) {
return CupertinoAlertDialog(
title: Text('iOS'),
content: const Text('This is the cupertino-style'),
actions: <Widget>[
CupertinoButton(
onPressed: Navigator.of(context).pop,
child: const Text('Close'),
),
],
);
}
}
FlutterEngineering.io
Factory Method with Default Method
AndroidAlertDialog().show();
IosAlertDialog().show();
✅ Decoding the Role of Design Patterns
✅ Factory Method Using Default Methods
Decorator by Chain Decorators
Execute Around Method Pattern
Strategy Pattern and Lightweight Strategies
Creating a Closed Hierarchy with Sealed
Summary
FlutterEngineering.io
FlutterEngineering.io
Decorator
FlutterEngineering.io
// Base Component
abstract class TextComponent {
Widget build(BuildContext context);
}
// Concrete Component
class SimpleText extends TextComponent {
final String text;
SimpleText(this.text);
@override
Widget build(BuildContext context) {
return Text(text);
}
}
Decorator
FlutterEngineering.io
FlutterEngineering.io
// Decorator
abstract class TextDecorator extends TextComponent {
TextDecorator(this.decoratedText);
final TextComponent decoratedText;
}
Decorator
FlutterEngineering.io
// Concrete Decorators
class BorderText extends TextDecorator {
BorderText(super.decoratedText);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: decoratedText.build(context),
);
}
}
Decorator
FlutterEngineering.io
// Concrete Decorator
class PaddingText extends TextDecorator {
PaddingText(super.decoratedText);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: decoratedText.build(context),
);
}
}
Decorator
FlutterEngineering.io
// Concrete Decorator
class BackgroundText extends TextDecorator {
BackgroundText(super.decoratedText);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellowAccent,
child: super.build(context),
);
}
}
Decorator
FlutterEngineering.io
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
TextComponent text = SimpleText('Hello, World!');
TextComponent borderedText = BorderText(text);
TextComponent paddedText = PaddingText(borderedText);
return MaterialApp(
home: Scaffold(
body: Center(
child: paddedText.build(context),
),
),
);
}
}
Decorator
Decorator by Chain Decorators
class ChainedTextDecorator extends TextComponent {
ChainedTextDecorator(this.baseComponent, this.decorators);
final TextComponent baseComponent;
final List<TextDecorator Function(TextComponent)> decorators;
@override
Widget build(BuildContext context) {
TextComponent currentComponent = baseComponent;
// Apply each decorator in sequence
for (final decorator in decorators) {
currentComponent = decorator(currentComponent);
}
return currentComponent.build(context);
}
}
FlutterEngineering.io
Decorator by Chain Decorators
final TextComponent chainedText = ChainedTextDecorator(
SimpleText('Hello, World!'),
[
(widget) => PaddingText(widget, padding: EdgeInsets.all(16.0)),
(widget) => BorderText(widget, color: Colors.blue, width: 3.0),
(widget) => BackgroundText(widget, backgroundColor: Colors.yellow[100]!),
],
);
FlutterEngineering.io
Decorator by Chain Decorators
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// ...
return MaterialApp(
home: Scaffold(
body: Center(
child: chainedText.build(context),
),
),
);
}
}
FlutterEngineering.io
✅ Decoding the Role of Design Patterns
✅ Factory Method Using Default Methods
✅ Decorator by Chain Decorators
Execute Around Method Pattern
Strategy Pattern and Lightweight Strategies
Creating a Closed Hierarchy with Sealed
Summary
FlutterEngineering.io
FlutterEngineering.io
Execute Around Method Pattern
FlutterEngineering.io
Execute Around Method Pattern
FlutterEngineering.io
class LoadingManager {
LoadingManager(this.context);
final BuildContext context;
}
Execute Around Method Pattern
class LoadingManager {
// ...
Future<void> executeAround(Future<void> Function() action) async {
try {
// Setup: Show the loading indicator
_showLoadingIndicator();
// Main Action: Execute the passed asynchronous function
await action();
} finally {
// Cleanup: Hide the loading indicator
_hideLoadingIndicator();
}
}
}
FlutterEngineering.io
Execute Around Method Pattern
class LoadingManager {
// ...
void _showLoadingIndicator() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Center(child: CircularProgressIndicator());
},
);
}
void _hideLoadingIndicator() {
Navigator.of(context).pop();
}
}
FlutterEngineering.io
Execute Around Method Pattern
class NetworkRequestExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () async {
final loadingManager = LoadingManager(context);
await loadingManager.executeAround(() async {
// Simulate a network request
await Future.delayed(Duration(seconds: 2));
print('Network request completed');
});
},
child: Text('Perform Network Request'),
),
),
);
}
}
FlutterEngineering.io
✅ Decoding the Role of Design Patterns
✅ Factory Method Using Default Methods
✅ Decorator by Chain Decorators
✅ Execute Around Method Pattern
Strategy Pattern and Lightweight Strategies
Creating a Closed Hierarchy with Sealed
Summary
FlutterEngineering.io
FlutterEngineering.io
Strategy Pattern and Lightweight Strategies
FlutterEngineering.io
Strategy Pattern and Lightweight Strategies
FlutterEngineering.io
abstract class PaymentStrategy {
void pay(double amount);
}
Strategy Pattern and Lightweight Strategies
class CreditCardPayment implements PaymentStrategy {
@override
void pay(double amount) {
print('Processing credit card payment of \$${amount.toStringAsFixed(2)}');
// Implement credit card payment logic
}
}
class PayPalPayment implements PaymentStrategy {
@override
void pay(double amount) {
print('Processing PayPal payment of \$${amount.toStringAsFixed(2)}');
// Implement PayPal payment logic
}
}
class CryptoPayment implements PaymentStrategy {
@override
void pay(double amount) {
print('Processing cryptocurrency payment of \$${amount.toStringAsFixed(2)}');
// Implement cryptocurrency payment logic
}
}
FlutterEngineering.io
Strategy Pattern and Lightweight Strategies
class PaymentContext {
PaymentStrategy? _strategy;
void setStrategy(PaymentStrategy strategy) {
_strategy = strategy;
}
void executePayment(double amount) {
_strategy?.pay(amount);
}
}
FlutterEngineering.io
Strategy Pattern and Lightweight Strategies
class _PaymentExampleState extends State<PaymentExample> {
final PaymentContext paymentContext = PaymentContext();
String selectedMethod = 'Credit Card';
final Map<String, PaymentStrategy> strategies = {
'Credit Card': CreditCardPayment(),
'PayPal': PayPalPayment(),
'Cryptocurrency': CryptoPayment(),
};
void _selectPaymentMethod(String method) {
setState(() {
selectedMethod = method;
paymentContext.setStrategy(strategies[method]!);
});
}
void _pay(double amount) {
paymentContext.executePayment(amount);
}
// ...
}
FlutterEngineering.io
Strategy Pattern and Lightweight Strategies
child: Column(
children: [
...strategies.keys.map((method) {
return Padding(
padding: EdgeInsets.all(16),
child: ElevatedButton(
onPressed: () => _selectPaymentMethod(method),
child: Text('Pay with $method'),
),
);
}).toList(),
TextButton(
onPressed: () => _pay(100.00),
child: Text('Proceed to Pay \$100.00'),
),
],
),
FlutterEngineering.io
✅ Decoding the Role of Design Patterns
✅ Factory Method Using Default Methods
✅ Decorator by Chain Decorators
✅ Execute Around Method Pattern
✅ Strategy Pattern and Lightweight Strategies
Creating a Closed Hierarchy with Sealed
Summary
FlutterEngineering.io
FlutterEngineering.io
Creating a Closed Hierarchy with Sealed
FlutterEngineering.io
Exhaustive?
Creating a Closed Hierarchy with Sealed
sealed class NetworkState {}
FlutterEngineering.io
Creating a Closed Hierarchy with Sealed
class LoadingState extends NetworkState {}
class SuccessState<T> extends NetworkState {
SuccessState(this.data);
final T data;
}
class ErrorState extends NetworkState {
ErrorState(this.errorMessage);
final String errorMessage;
}
class EmptyState extends NetworkState {}
FlutterEngineering.io
Creating a Closed Hierarchy with Sealed
class ApiService {
Future<NetworkState> fetchData() async {
await Future.delayed(Duration(seconds: 2));
// Simulate different outcomes
final success = true;
if (success) {
final data = ['Item 1', 'Item 2', 'Item 3'];
if (data.isEmpty) {
return EmptyState();
} else {
return SuccessState(data);
}
} else {
return ErrorState('Failed to fetch data.');
}
}
}
FlutterEngineering.io
Creating a Closed Hierarchy with Sealed
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
final state = snapshot.data!;
return Center(
child: switch (state) {
LoadingState() => CircularProgressIndicator(),
SuccessState dataState => ListView.builder(),
ErrorState errorState => Column(),
EmptyState() => Text()
},
);
} else {
return Center(child: Text('Something went wrong.'));
}
},
FlutterEngineering.io
Creating a Closed Hierarchy with Sealed
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
final state = snapshot.data!;
return Center(
child: switch (state) {
SuccessState dataState => ListView.builder(),
ErrorState errorState => Column(),
EmptyState() => Text()
},
);
} else {
return Center(child: Text('Something went wrong.'));
}
},
FlutterEngineering.io
✅ Decoding the Role of Design Patterns
✅ Factory Method Using Default Methods
✅ Decorator by Chain Decorators
✅ Execute Around Method Pattern
✅ Strategy Pattern and Lightweight Strategies
✅ Creating a Closed Hierarchy with Sealed
Summary
FlutterEngineering.io
FlutterEngineering.io
✅ Decoding the Role of Design Patterns
✅ Factory Method Using Default Methods
✅ Decorator by Chain Decorators
✅ Execute Around Method Pattern
✅ Strategy Pattern and Lightweight Strategies
✅ Creating a Closed Hierarchy with Sealed
✅ Summary
FlutterEngineering.io
FlutterEngineering.io
bit.ly/4cOcLWE
Slides