Summary: In this tutorial, you’ll learn how to use the Dart interface class to create contracts that other classes must adhere to.
What is an interface class? #
In Dart, an interface is a contract between classes. Additionally, every class is an implicit interface.
Dart 3 introduced an interface modifier that allows you to define a contract between classes.
When you define a class with the interface modifier, you create a contract that other classes must implement, but cannot extend.
Here’s the basic syntax for defining an interface class:
abstract interface class InterfaceName {
methodName1();
methodName2();
}Code language: PHP (php)In this syntax:
- First, specify the interface name after the
interface classkeywords. If you don’t provide implementations for methods in the interface, you need to mark it asabstract. - Second, define methods for interfaces. These methods may have implementation or not, depending on whether
abstractmodifier is available.
If a class wants to use the InterfaceName, it must implement all the methods of the interface, including methodName1 and methodName2.
For example, the following defines an interface called Logger:
abstract interface class Logger {
void log(String message);
}
class ConsoleLogger implements Logger {
@override
void log(String message) {
print('Console: $message');
}
}
void main() {
Logger logger = ConsoleLogger();
logger.log('Hi');
}Code language: JavaScript (javascript)Output:
Console: HiCode language: HTTP (http)Interface with Method Implementation #
The following example redefines the Logger interface with method implementation:
interface class Logger {
void log(String message) {
print('Default log: $message');
}
}
class FileLogger implements Logger {
@override
void log(String message) {
print('Writing to file: $message'); // Must still override
}
}
void main() {
Logger logger = FileLogger();
logger.log('Hi');
}Code language: JavaScript (javascript)Output:
Writing to file: HiHow it works.
First, define the Logger interface that has a method log() with an implementation:
interface class Logger {
void log(String message) {
print('Default log: $message');
}
}Code language: JavaScript (javascript)Second, define the FileLogger class that implements the Logger interface:
class FileLogger implements Logger {
@override
void log(String message) {
print('Writing to file: $message'); // Must still override
}
}Code language: PHP (php)Third, create a FileLogger instance and call the log() method in the main() function:
void main() {
Logger logger = FileLogger();
logger.log('Hi');
}Code language: JavaScript (javascript)Dart Interface class example #
Let’s model a payment system using interface class:
abstract interface class PaymentGateway {
Future<void> charge(double amount);
}
class StripePayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using Stripe...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}
class PayPalPayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using PayPal...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}
enum Gateway {
stripe,
paypal
}
class PaymentGatewayFactory {
static PaymentGateway create(Gateway name) {
switch(name) {
case Gateway.stripe: return StripePayment();
case Gateway.paypal: return PayPalPayment();
}
}
}
void main() {
var pg = PaymentGatewayFactory.create(Gateway.stripe);
pg.charge(10.0);
}Code language: PHP (php)How it works.
Step 1. Define a PaymentGateway interface with one method charge():
abstract interface class PaymentGateway {
Future<void> charge(double amount);
}Code language: JavaScript (javascript)Step 2. Implement the PaymentGateway using Stripe API (simulated only):
class StripePayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using Stripe...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}Code language: PHP (php)Step 3. Implement the PaymentGateway using PayPal API (simulated only):
class PayPalPayment implements PaymentGateway {
@override
Future<void> charge(double amount) async {
print('Charging \$${amount} using PayPal...');
await Future.delayed(Duration(seconds: 1));
print('Payment complete.');
}
}Code language: PHP (php)Step 4. Define an enum that includes the payment gateway name:
enum Gateway {
stripe,
paypal
}Step 5. Define a class that creates an instance of a class that implements the PaymentGateway interface:
class PaymentGatewayFactory {
static PaymentGateway create(Gateway name) {
switch(name) {
case Gateway.stripe: return StripePayment();
case Gateway.paypal: return PayPalPayment();
}
}
}Code language: JavaScript (javascript)Step 6. Create a PaymentGateway instance and call the charge() method:
void main() {
var pg = PaymentGatewayFactory.create(Gateway.stripe);
pg.charge(10.0);
}Code language: JavaScript (javascript)In practice, you can read the gateway name from a configuration file and dynamically swap between the payment gateways.
If you want to add a payment gateway, you can:
- First, define a class that implements the PaymentGateway interface.
- Second, add the payment gateway name to the enum.
- Third, swap it in the main() method. If you use a configuration, you don’t need to change the main() method.
Summary #
- Use an interface class to define a contract between classes.
- Classes can implement an interface class, but cannot extend it.