Detailed Explanation of the Use of mixin in Flutter

  • 2021-12-04 11:11:47
  • OfStack

What is mixin

How should mixin be understood? For me who was born in Java department, this is a new concept, and the introduction of various materials has not found a clear definition. From my personal understanding, I can think of it as an interface in Kotlin (the difference between Kotlin and Java is that it can bring non-abstract attributes and methods), while multiple mixin can cover each other to realize combination, which provides great flexibility and can also achieve the effect similar to multiple inheritance.

Page table page

This is a common display data, pull-up and load a list of more data.

One of the types is List<T> The data list listData has an page data for paging, an isLoading for determining whether data is being loaded, and an scrollController for the list controller

If there are a large number of such pages, you can use mixin to deal with them, which inevitably leads to a lot of duplicate code


import 'package:flutter/material.dart';
import 'package:flutter_app/app/model/ListViewJson.dart';
import 'package:flutter_app/app/shared/api/api.dart';
import 'package:dio/dio.dart';
import 'dart:convert';

import 'package:flutter_app/app/shared/mixins/list_more_data_mixin.dart';

///  List page 
class RecommendView extends StatefulWidget {
 @override
 _RecommendViewState createState() => _RecommendViewState();
}

class _RecommendViewState
 extends ListMoreDataBase<ListViewJsonData, RecommendView>
 with ListMoreDataMixin<ListViewJsonData, RecommendView> {
 @override
 Future<List<ListViewJsonData>> getData() async {
 String data = await DioUtils.postHttp(
 "api/getOneLevel",
 parameters: FormData.fromMap({
 'page': page,
 'limit': '10',
 }),
 );
 ListViewJson _json = ListViewJson.fromJson(json.decode(data));
 return _json.data;
 }

 @override
 void initState() {
 print('init widget');
 super.initState();
 }

 @override
 void dispose() {
 print('dispose widget');
 super.dispose();
 }

 @override
 Widget build(BuildContext context) {
 return Scaffold(
 backgroundColor: Colors.white,
 appBar: AppBar(title: Text(' Return ')),
 body: Stack(
 children: <Widget>[
  NotificationListener<ScrollNotification>(
  onNotification: onNotification,
  child: ListView.builder(
  controller: scrollController,
  itemCount: listData.length,
  itemBuilder: (BuildContext context, int index) =>
   TeamListItem(listData[index]),
  ),
  ),
  isLoading ? Center(child: CircularProgressIndicator()) : Container()
 ],
 ),
 );
 }
}

mixin


import 'package:flutter/material.dart';

abstract class ListMoreDataBase<T, K extends StatefulWidget> extends State<K> {
 ///  Get asynchronous data 
 Future<List<T>> getData();
}

///  In 
mixin ListMoreDataMixin<T, K extends StatefulWidget> on ListMoreDataBase<T, K> {
 @override
 void initState() {
 print('init');
 super.initState();
 initData();
 }

 @override
 void dispose() {
 print('dispose');
 super.dispose();
 scrollController?.dispose();
 }

 ///  Data list 
 List<T> listData = [];

 ///  Paging 
 int page = 1;

 ///  Is data being loaded 
 bool isLoading = false;

 ///  Scroll bar controller 
 ScrollController scrollController = ScrollController();

 ///  Initialization data 
 Future<void> initData() async {
 setState(() {
 isLoading = true;
 });

 List<T> data = await getData();
 if (!mounted) return;
 setState(() {
 listData = data;
 isLoading = false;
 });
 }

 ///  Pull up and load more 
 Future<void> loadMore() async {
 setState(() {
 isLoading = true;
 page += 1;
 });

 List<T> data = await getData();

 if (data.isEmpty) {
 page--;
 }

 setState(() {
 listData.addAll(data);
 isLoading = false;
 });
 }

 bool canLoadMore(ScrollNotification scroll) {
 return !isLoading &&
 scroll.metrics.maxScrollExtent <= scrollController.offset;
 }

 bool onNotification(ScrollNotification scroll) {
 if (canLoadMore(scroll)) {
 loadMore();
 }
 return true;
 }
}

Note:

dart is single inheritance In the class, you can override the properties and methods of mixin, and you can also call the properties and methods of miixn with super The lifecycle above prints init widget-- > init - > dispose widget - > dispose

ps: From simple to complex, the following demonstrates the use of mixin in Dart

The simplest mixin


mixin TestMixin {
 void test() {
 print('test');
 }
 int testInt = 1;
 void test2();
}
 
class Test with TestMixin {
 @override
 test2() {
 print('test2');
 }
}

void main() {
 Test().test();  // test
 print(Test().testInt); // 1
 Test().test2();  // test2
}

mixin itself can be abstract, which can define various method properties, or it can be abstract, which can be implemented by subsequent classes

mixin based on a certain type


class BaseObject {
 void method() {
 print('call method');
 }
}
mixin TestMixin on BaseObject{
 void test() {
 print('test');
 }
 int testInt = 1;
 void test2() {
 method();
 }
}
 
class Test extends BaseObject with TestMixin {
}
 
void main() {
 Test().test();  // test
 print(Test().testInt); // 1
 Test().test2();  // call method
}

When the on keyword is used, it means that the mixin can only be used in the subclass of that class, so it is obvious that the methods and properties defined by that class can be called in mixin

Multiple mixin


mixin TestMixin {
 void test() {
 print('test');
 }
 
 int testInt = 1;
 
 void test2();
}
 
mixin TestMixin2 {
 int testInt = 2;
 
 void test3() {
 print('test3');
 }
}
 
class Test with TestMixin, TestMixin2 {
 @override
 test2() {
 print('test2');
 }
}
 
void main() {
 Test().test();  // test
 print(Test().testInt); // 2
 Test().test2();  // test2
 Test().test3();  // test3
}

If you change the order of TestMixin and TestMixin2 by one:


mixin TestMixin {
 void test() {
 print('test');
 }
 
 int testInt = 1;
 
 void test2();
}
 
mixin TestMixin2 {
 int testInt = 2;
 
 void test3() {
 print('test3');
 }
}
 
class Test with TestMixin2, TestMixin {
 @override
 test2() {
 print('test2');
 }
}
 
void main() {
 Test().test();  // test
 print(Test().testInt); // 1
 Test().test2();  // test2
 Test().test3();  // test3
}

If the conflicting part of mixin exists, the former part will be overwritten, and the former part will be retained if there is no conflict. Therefore, the latter mixin can modify the first part of the logic of the former mixin, and the overwriting can be realized without direct inheritance, thus avoiding more complex inheritance relationship


" Multiple inheritance "
mixin TestMixin on BaseClass {
 void init() {
 print('TestMixin init start');
 super.init();
 print('TestMixin init end');
 }
}
 
mixin TestMixin2 on BaseClass {
 void init() {
 print('TestMixin2 init start');
 super.init();
 print('TestMixin2 init end');
 }
}
 
class BaseClass {
 void init() {
 print('Base init');
 }
 BaseClass() {
 init();
 }
}
 
class TestClass extends BaseClass with TestMixin, TestMixin2 {
 
 @override
 void init() {
 print('TestClass init start');
 super.init();
 print('TestClass init end');
 
 }
}
 
void main() {
 TestClass();
 /// TestClass init start
 /// TestMixin2 init start
 /// TestMixin init start
 /// Base init
 /// TestMixin init end
 /// TestMixin2 init end
 /// TestClass init end
}

A little bit around, as you can see, this has actually achieved the effect that multiple inheritance can achieve, which is cumbersome to write and less error-prone to some extent (compared with C + +). . . The source code has the best and most complex example-WidgetsFlutterBinding, which is defined as follows:


class WidgetsFlutterBinding 
extends BindingBase with GestureBinding, 
ServicesBinding, 
SchedulerBinding, 
PaintingBinding, 
SemanticsBinding,
 RendererBinding,
 WidgetsBinding 
{
}

Specific WidgetsFlutterBinding analysis is gone, see the source code by yourself ~ ~

Summarize


Related articles: