What is the Proxy Pattern?
The Proxy Pattern is a structural design pattern that:
Provides a substitute or placeholder for another object to control access to it.
It’s like placing a gatekeeper between the client and the real object. This “proxy” can add additional logic (like caching, lazy initialization, access control, logging…) before forwarding the request.
Real-World Analogy
Think of a security guard at the door of a high-security room:
- Visitors don’t talk to the people inside directly.
- They go through the guard who checks credentials, logs the visit, and only then allows access.
In software, the guard is your proxy.
When to Use the Proxy Pattern
- You want to control access to an object (security, permissions, etc.).
- You want to add lazy initialization or caching logic.
- You want to add logging or monitoring without touching the original object.
- The actual object is expensive to create or located remotely (e.g., over a network).
Types of Proxies
| Proxy Type | Description |
|---|---|
| Virtual Proxy | Controls access to resource-heavy objects (e.g., images) |
| Protection Proxy | Checks access rights (e.g., auth, permissions) |
| Remote Proxy | Controls access to remote objects (e.g., via API) |
| Smart Proxy | Adds extra behaviors like caching, counting, logging |
Dart Example: Virtual Proxy for an Image Viewer
Suppose you are building an image viewer app. Large images are expensive to load, so you want to delay loading until needed.
Step 1: Define the Subject Interface
abstract class Image {
void display();
}
Step 2: Create the Real Object
class RealImage implements Image {
final String filename;
RealImage(this.filename) {
_loadFromDisk();
}
void _loadFromDisk() {
print("Loading image from disk: $filename");
}
@override
void display() {
print("Displaying: $filename");
}
}
Step 3: Create the Proxy Object
class ProxyImage implements Image {
final String filename;
RealImage? _realImage;
ProxyImage(this.filename);
@override
void display() {
_realImage ??= RealImage(filename); // lazy load
_realImage!.display();
}
}
Step 4: Client Code
void main() {
Image image1 = ProxyImage("mountains.jpg");
Image image2 = ProxyImage("beach.jpg");
print("Image1 will be displayed:");
image1.display(); // Loads + displays
print("\nImage1 again:");
image1.display(); // Just displays (already loaded)
print("\nImage2 will be displayed:");
image2.display(); // Loads + displays
}
Output
Image1 will be displayed:
Loading image from disk: mountains.jpg
Displaying: mountains.jpg
Image1 again:
Displaying: mountains.jpg
Image2 will be displayed:
Loading image from disk: beach.jpg
Displaying: beach.jpg
Key Benefits of Proxy Pattern
| Benefit | Description |
|---|---|
| Lazy Loading | Load resources only when necessary |
| Access Control | Prevent unauthorized use |
| Logging | Track usage without modifying original object |
| Caching | Store computed results to avoid repetition |
Drawbacks
- Adds extra layers of abstraction.
- May become hard to maintain if overused.
- Performance overhead if proxies are nested or complex.
Dart & Flutter Use Cases
- Image.network in Flutter uses internal proxying and caching.
- A Repository class can use a proxy to check authentication before fetching data.
- Analytics proxies can log events when users perform actions without modifying main UI logic.
- You can create a secure proxy to protect admin-only services.
Summary
| Property | Proxy Pattern |
|---|---|
| Pattern Type | Structural |
| Core Idea | Substitute the real object with a handler |
| Key Use | Control access, lazy load, caching |
| In Flutter | Great for optimizing performance |
Comparison with Other Patterns
| Pattern | Difference |
|---|---|
| Facade | Simplifies subsystem usage |
| Decorator | Adds behavior dynamically without hiding object |
| Adapter | Converts one interface to another |
| Proxy | Controls access to original object (can wrap or delay it) |


