一种flutter应用的跨平台适配解决方案
采用flutter开发APP已经1年多了,平时开发的过程中踩过很多坑,今天想分享一个flutter应用跨平台适配的解决方案,目的是让你的flutter应用可以灵活高效的自动适配各种平台,实现UI最大化复用,业务逻辑代码在不同平台间能够共享。
下图描述了flutter-adapter的整体实现方案,我们在APP的入口处设置当前APP所运行的平台,这个平台可以在实际使用的时候动态设置,然后对于每个widget,我们只需要实现特定平台的样式适配即可。个别widget在不同平台的表现可能仅仅是部分属性的数值不同而已,对于属性值的垮平台适配我们提供了一个通用的函数superObjectAdapter去解决这个问题。

不同平台适配效果



使用方式
flutter_adapter插件内置了3类平台,分别是:手机(TEAdaptPlatform.phone)、pad横屏(TEAdaptPlatform.padLandscape)、pad竖屏(TEAdaptPlatform.padPortrait)
。如果你只适配部分平台,那么只需要对待适配的widget实现特定平台的build函数即可,其他未适配的平台默认会返回Phone的样式。
使用的时候只需要在APP的入口处采用ScreenAdaptWidget,然后设置当前APP需要适配的平台名称即可。
如果你需要扩展适配的平台,对于StatelessWidget只需要实现一个继承自FlexibleStatelessWidget的抽象类,然后实现新平台的build函数并注册该平台即可;对于StatefulWidget只需要实现一个继承自FlexibleState的抽象类,然后实现新平台的build函数并注册该平台即可。
插件使用示例:
ScreenAdaptWidget(
platform: TEAdaptPlatform.phone.toString(),
child: 任意widget
)),
如果你的某个StatelessWidget需要适配特定平台,只需要将该widget继承自FlexibleStatelessWidget,然后实现特定平台的build函数即可。
StatelessWidget适配示例:
class MyStatelessPage extends FlexibleStatelessWidget {
@override
Widget buildPhone(BuildContext context) {
return Text('Phone',style: TextStyle(fontSize: 18.0),);
}
@override
Widget buildPadPortrait(BuildContext context) {
return Text('PadPortrait',style: TextStyle(fontSize: 22.0),);
}
@override
Widget buildPadLandscape(BuildContext context) {
return Text('PadLandscape',style: TextStyle(fontSize: 30.0),);
}
}
StatefulWidget适配示例:
如果你的某个StatefulWidget需要适配特定平台,只需要将该StatefulWidget对应的的State继承自FlexibleState,然后实现特定平台的build函数即可,例如:
class MyStatefulPageState extends FlexibleState<MyStatefulPage> {
@override
Widget buildPhone(BuildContext context) {
return Text('Phone',style: TextStyle(fontSize: 18.0),);
}
@override
Widget buildPadPortrait(BuildContext context) {
return Text('PadPortrait',style: TextStyle(fontSize: 22.0),);
}
@override
Widget buildPadLandscape(BuildContext context) {
return Text('PadLandscape',style: TextStyle(fontSize: 30.0),);
}
}
普通Widget适配示例:
如果你的某个widget仅仅需要在不同平台中改变个别属性的值,那么只需要对特定的属性值进行跨平台适配即可,flutter_adapter提供了superObjectAdapter函数来解决属性值的跨平台适配难题。
class MyNormalPage extends StatelessWidget {
final String textStr;
MyNormalPage(this.textStr);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('page normal'),
),
body: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.only(bottom: 30.0),
width: double.infinity,
height: 100.0,
color: superObjectAdapter(context, {TEAdaptPlatform.phone.toString(): Colors.yellow, TEAdaptPlatform.padPortrait.toString(): Colors.greenAccent}),
child: Center(
child: Text(
'$textStr ${superObjectAdapter(context, {
TEAdaptPlatform.phone.toString(): "[Phone]",
TEAdaptPlatform.padPortrait.toString(): "[PadPortrait]"
})}',
style: TextStyle(
fontSize: superObjectAdapter(context, {TEAdaptPlatform.phone.toString(): 18.0, TEAdaptPlatform.padPortrait.toString(): 38.0}),
color: Colors.black),
),
),
),
],
),
);
}
}


扩展需要适配的平台:
插件内置的3个平台在实际使用的过程中可能不够用,因此我们提供了用户自定义平台的适配解决方案。
StatelessWidget适配新平台:
对于StatelessWidget只需要实现一个继承自FlexibleStatelessWidget的抽象类,然后实现新平台的build函数,之后注册该平台即可。
abstract class CustomFlexibleStatelessWidget extends FlexibleStatelessWidget {
@protected
Widget buildNewPlatform(BuildContext context) {
return buildPhone(context); // by default, you can return the phone's style
}
@protected
void initAdapter() {
super.initAdapter();
addAdapter(Constant.newPlatform, buildNewPlatform);// register new Platform
}
}
StatelessWidget适配新平台示例:
class MyStatelessPage extends CustomFlexibleStatelessWidget {
@override
Widget buildPhone(BuildContext context) {
return Text('Phone',style: TextStyle(fontSize: 18.0),);
}
@override
Widget buildPadPortrait(BuildContext context) {
return Text('PadPortrait',style: TextStyle(fontSize: 22.0),);
}
@override
Widget buildNewPlatform(BuildContext context) {
return Text('buildNewPlatform',style: TextStyle(fontSize: 30.0),);
}
}
StatefulWidget适配新平台:
对于StatefulWidget只需要实现一个继承自FlexibleState的抽象类,然后实现新平台的build函数,之后注册该平台即可。
abstract class CustomFlexibleState<T extends StatefulWidget> extends FlexibleState<T> {
@protected
Widget buildNewPlatform(BuildContext context) {
return buildPhone(context); // by default, you can return the phone's style
}
@protected
void initAdapter() {
super.initAdapter();
addAdapter(Constant.newPlatform, buildNewPlatform);// register new Platform
}
}
StatefulWidget适配新平台示例:
class MyStatefulPageState extends CustomFlexibleState<MyStatefulPage> {
@override
Widget buildPhone(BuildContext context) {
return Text('Phone',style: TextStyle(fontSize: 18.0),);
}
@override
Widget buildPadPortrait(BuildContext context) {
return Text('PadPortrait',style: TextStyle(fontSize: 22.0),);
}
@override
Widget buildNewPlatform(BuildContext context) {
return Text('NewPlatform',style: TextStyle(fontSize: 30.0),);
}
}
