作者:buaashui
博客:https://www.jianshu.com/p/143fb2111f96
前言
采用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),); }}
用户自定义平台
本项目已经开源,开源地址:
Flutter-adapter(https://github.com/buaashuai/flutter_adapter )
近期文章:
今日问题:
多平台都跨了,怎么能缺屏幕适配?
专属升级社区:《这件事情,我终于想明白了》