Debug School

rakesh kumar
rakesh kumar

Posted on

explain the use of Mixins in flutter

Why we use mixins
Application of mixins

Why we use mixins

Mixins in Flutter (and Dart) are a way to reuse a class’s code in multiple class hierarchies. Mixins allow you to add functionality to classes without needing inheritance, making it easier to maintain and reuse code. They are used to encapsulate and share behavior between classes.

Key Points about Mixins

Code Reusability: Mixins allow you to reuse methods and properties in multiple classes.
Avoid Multiple Inheritance: Dart does not support multiple inheritance, but mixins offer a way to achieve similar behavior.
Flexibility: You can mix in multiple mixins into a single class, providing a flexible way to combine functionality.

In other words mixins are normal classes from which we can borrow methods(or variables) from without extending the class. In dart we can do this by

For Java developers, it is a completely new concept to learn as Java does not supports multiple inheritance or mixins. Java tries to make up for this by using Interfaces, but that is not as useful or flexible as mixins.

class B {  //B is not allowed to extend any other class other than object
  method(){
     ....
  }
}

class A with B {
  ....
     ......
}
void main() {
  A a = A();
  a.method();  //we got the method without inheriting B
}
Enter fullscreen mode Exit fullscreen mode

Defining a Mixin

In Dart, a mixin is created using the mixin keyword.

Example: LoggerMixin
Let's define a mixin for logging purposes.

import 'package:logger/logger.dart';

// Define the LoggerMixin
mixin LoggerMixin {
  final Logger _logger = Logger();

  void logInfo(String message) {
    _logger.i(message);
  }

  void logError(String message) {
    _logger.e(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Using the LoggerMixin
To use the mixin, you simply include it in the class definition using the with keyword.

class MyClass with LoggerMixin {
  final String name;

  MyClass(this.name);

  void doSomething() {
    logInfo('$name is doing something.');
    try {
      // Simulate an error
      int result = 1 ~/ 0;
    } catch (e) {
      logError('Error occurred: $e');
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Complete Example in a Flutter App
Let's put everything together and create a simple Flutter app that uses the LoggerMixin.

Step 1: Add Dependencies
Add the logger package to your pubspec.yaml file.

dependencies:
  flutter:
    sdk: flutter
  logger: ^1.0.0
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the LoggerMixin
Create a file logger_mixin.dart and define the LoggerMixin.

// logger_mixin.dart
import 'package:logger/logger.dart';

mixin LoggerMixin {
  final Logger _logger = Logger();

  void logInfo(String message) {
    _logger.i(message);
  }

  void logError(String message) {
    _logger.e(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Class Using the LoggerMixin
Create a file my_class.dart and define the MyClass which uses the LoggerMixin.

// my_class.dart
import 'logger_mixin.dart';

class MyClass with LoggerMixin {
  final String name;

  MyClass(this.name);

  void doSomething() {
    logInfo('$name is doing something.');
    try {
      // Simulate an error
      int result = 1 ~/ 0;
    } catch (e) {
      logError('Error occurred: $e');
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Integrate MyClass in Flutter App
Update your main.dart to use MyClass and display the log output.

import 'package:flutter/material.dart';
import 'my_class.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final MyClass myInstance = MyClass('TestInstance');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Mixin Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            myInstance.doSomething();
          },
          child: Text('Do Something'),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation

LoggerMixin: This mixin provides logging functionality through logInfo and logError methods.
MyClass: This class uses the LoggerMixin to gain logging capabilities. It logs messages when performing actions and handling errors.
Flutter App: The main Flutter app integrates MyClass and demonstrates how to use the logging functionality. When you press the "Do Something" button, the doSomething method is called, and log messages are printed to the console.
Running the Example
When you run the Flutter app and press the "Do Something" button, you should see log messages in the console, demonstrating the logging functionality added to MyClass via the LoggerMixin.

This example showcases how mixins in Flutter can be used to encapsulate and reuse functionality across different classes, promoting clean and maintainable code.

Application of mixins

LoggerMixin
A mixin to provide logging capabilities.

import 'package:logger/logger.dart';

mixin LoggerMixin {
  final Logger _logger = Logger();

  void logInfo(String message) {
    _logger.i(message);
  }

  void logError(String message) {
    _logger.e(message);
  }
}

class MyClass with LoggerMixin {
  void doSomething() {
    logInfo('Doing something');
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. ValidationMixin A mixin to provide form validation methods.
mixin ValidationMixin {
  bool isValidEmail(String email) {
    return email.contains('@');
  }

  bool isValidPassword(String password) {
    return password.length > 6;
  }
}

class LoginForm with ValidationMixin {
  final String email;
  final String password;

  LoginForm(this.email, this.password);

  bool validate() {
    return isValidEmail(email) && isValidPassword(password);
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. DisposalMixin A mixin to provide a method to dispose resources.
mixin DisposalMixin {
  final List<void Function()> _disposeCallbacks = [];

  void addDisposeCallback(void Function() callback) {
    _disposeCallbacks.add(callback);
  }

  void dispose() {
    for (var callback in _disposeCallbacks) {
      callback();
    }
  }
}

class MyResource with DisposalMixin {
  void initialize() {
    // Initialize resources
    addDisposeCallback(() {
      // Dispose resource
      print('Resource disposed');
    });
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. StateHelperMixin A mixin to provide common state management methods for stateful widgets.
import 'package:flutter/material.dart';

mixin StateHelperMixin<T extends StatefulWidget> on State<T> {
  void showSnackbar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
  }
}

class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> with StateHelperMixin<MyStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('State Helper Mixin Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            showSnackbar('Hello, World!');
          },
          child: Text('Show Snackbar'),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. ConnectivityMixin A mixin to check network connectivity.
import 'package:connectivity_plus/connectivity_plus.dart';

mixin ConnectivityMixin {
  final Connectivity _connectivity = Connectivity();

  Future<bool> isConnected() async {
    var connectivityResult = await _connectivity.checkConnectivity();
    return connectivityResult != ConnectivityResult.none;
  }
}

class MyService with ConnectivityMixin {
  Future<void> fetchData() async {
    if (await isConnected()) {
      // Fetch data
      print('Fetching data...');
    } else {
      print('No internet connection');
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. NavigationMixin A mixin to provide navigation methods.
import 'package:flutter/material.dart';

mixin NavigationMixin<T extends StatefulWidget> on State<T> {
  void navigateTo(Widget page) {
    Navigator.of(context).push(MaterialPageRoute(builder: (context) => page));
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with NavigationMixin<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Navigation Mixin Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            navigateTo(SecondPage());
          },
          child: Text('Go to Second Page'),
        ),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Page'),
      ),
      body: Center(
        child: Text('This is the second page'),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. ErrorHandlingMixin A mixin to provide error handling methods.
mixin ErrorHandlingMixin {
  void handleError(dynamic error) {
    print('An error occurred: $error');
  }
}

class MyErrorProneClass with ErrorHandlingMixin {
  void riskyOperation() {
    try {
      // Simulate an error
      throw Exception('Something went wrong');
    } catch (error) {
      handleError(error);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

LifecycleMixin
A mixin to provide lifecycle methods, especially useful for handling logic when widgets are initialized and disposed.

import 'package:flutter/widgets.dart';

mixin LifecycleMixin<T extends StatefulWidget> on State<T> {
  @override
  void initState() {
    super.initState();
    onInit();
  }

  @override
  void dispose() {
    onDispose();
    super.dispose();
  }

  void onInit() {
    // Override this method in the class using this mixin
  }

  void onDispose() {
    // Override this method in the class using this mixin
  }
}

class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> with LifecycleMixin<MyStatefulWidget> {
  @override
  void onInit() {
    super.onInit();
    print('Widget Initialized');
  }

  @override
  void onDispose() {
    super.onDispose();
    print('Widget Disposed');
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. CacheMixin A mixin to provide caching capabilities for data.
mixin CacheMixin<T> {
  final Map<String, T> _cache = {};

  void cacheData(String key, T value) {
    _cache[key] = value;
  }

  T? getCachedData(String key) {
    return _cache[key];
  }

  void clearCache() {
    _cache.clear();
  }
}

class DataService with CacheMixin<String> {
  void fetchData() {
    // Fetch and cache data
    cacheData('key1', 'value1');
  }

  String? retrieveData() {
    return getCachedData('key1');
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. AnimationMixin A mixin to provide common animation utilities.
import 'package:flutter/animation.dart';

mixin AnimationMixin<T extends StatefulWidget> on State<T>, SingleTickerProviderStateMixin<T> {
  late AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    )..forward();
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }
}

class AnimatedWidgetExample extends StatefulWidget {
  @override
  _AnimatedWidgetExampleState createState() => _AnimatedWidgetExampleState();
}

class _AnimatedWidgetExampleState extends State<AnimatedWidgetExample> with AnimationMixin<AnimatedWidgetExample> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FadeTransition(
          opacity: animationController,
          child: Text('Hello, World!'),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. LocalizationMixin A mixin to provide localization capabilities.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

mixin LocalizationMixin {
  String getTranslatedText(BuildContext context, String key) {
    // Implement your localization logic here
    // For example, using a localization package
    return key;
  }
}

class LocalizedWidget extends StatelessWidget with LocalizationMixin {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(getTranslatedText(context, 'title')),
      ),
      body: Center(
        child: Text(getTranslatedText(context, 'hello_world')),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. ThemeMixin A mixin to provide theme management.
import 'package:flutter/material.dart';

mixin ThemeMixin<T extends StatefulWidget> on State<T> {
  ThemeData get currentTheme => Theme.of(context);

  bool get isDarkMode => currentTheme.brightness == Brightness.dark;
}

class ThemedWidget extends StatefulWidget {
  @override
  _ThemedWidgetState createState() => _ThemedWidgetState();
}

class _ThemedWidgetState extends State<ThemedWidget> with ThemeMixin<ThemedWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Themed Widget'),
      ),
      body: Center(
        child: Text(
          isDarkMode ? 'Dark Mode' : 'Light Mode',
          style: TextStyle(color: isDarkMode ? Colors.white : Colors.black),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. StateManagementMixin A mixin to provide state management utilities.
mixin StateManagementMixin<T extends StatefulWidget> on State<T> {
  bool _isLoading = false;

  bool get isLoading => _isLoading;

  void setLoading(bool loading) {
    setState(() {
      _isLoading = loading;
    });
  }
}

class StateManagedWidget extends StatefulWidget {
  @override
  _StateManagedWidgetState createState() => _StateManagedWidgetState();
}

class _StateManagedWidgetState extends State<StateManagedWidget> with StateManagementMixin<StateManagedWidget> {
  void loadData() async {
    setLoading(true);
    await Future.delayed(Duration(seconds: 2));
    setLoading(false);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('State Management Mixin'),
      ),
      body: Center(
        child: isLoading
            ? CircularProgressIndicator()
            : ElevatedButton(
                onPressed: loadData,
                child: Text('Load Data'),
              ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. PermissionMixin A mixin to handle permission requests.
import 'package:permission_handler/permission_handler.dart';

mixin PermissionMixin {
  Future<bool> requestPermission(Permission permission) async {
    final status = await permission.request();
    return status == PermissionStatus.granted;
  }

  Future<bool> checkPermission(Permission permission) async {
    final status = await permission.status;
    return status == PermissionStatus.granted;
  }
}

class PermissionWidget extends StatelessWidget with PermissionMixin {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Permission Mixin Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            if (await requestPermission(Permission.camera)) {
              print('Camera permission granted');
            } else {
              print('Camera permission denied');
            }
          },
          child: Text('Request Camera Permission'),
        ),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

reference1
reference2

Top comments (0)