Flutter中的Cupertino和Material小部件
介绍
最近也在想,要如何实现FLutter在Android平台上显示Material风格的UI,在ios平台显示Cupertino风格的UI,刚好看到一篇外国文章:Do Flutter apps dream of platform aware widgets?,讲的就是这个问题,于是自己也跟着实现了一下。总的来说,不难,主要是抽象出一个BasePlatformWidget就可以了。

背景
Flutter是一个新的跨平台应用程序开发框架。Flutter中有两套UI组件,分别适用于ios和Android,分别叫做Cupertino和Material。

但是如果希望在各自的平台上继续保持原来的风格的话,比如在Android手机上保持Material风格,在ios上保持Cupertino风格的话,那应该如何实现呢?

如果要写两套代码的话,就太麻烦了。因为两套代码其实有大部分东西是一样的,只是因为风格不一样,而用了不一样的Widget而已。在FLutter中还要注意的一点问题就是,某一些小部件需要一个属于同一“平台特定”库的祖先。举个例子,RaisedButton和Switch属于Material包库,它需要一个Material小部件作为其祖先之一,如果没有的话,则运行的时候会报出下面的错误。

image
实现思路:我们可以创建个抽象的组件,并让它根据其运行的平台,显示不同风格的Widget。

代码实现
创建一个抽象类,子类只需要重写对应平台的抽象方法就可以了。

/**

  • 会根据平台,去适配所在平台的小部件
  • Flutter中包含了适用于IOS和Android的两套原生小部件,名为Cupertino和Material
    */
    abstract class BasePlatformWidget<A extends Widget, I extends Widget>
    extends StatelessWidget {
    A createAndroidWidget(BuildContext context);

    I createIosWidget(BuildContext context);

    @override
    Widget build(BuildContext context) {
    /**如果是IOS平台,返回ios风格的控件

    • Android和其他平台都返回materil风格的控件
      */
      if (Platform.isIOS) {
      return createIosWidget(context);
      }
      return createAndroidWidget(context);
      }
      }
      下面根据这个基础的平台类,去创建一些基本的Widget。比如去创建一个在Android平台显示AppBar,在ios平台显示CupertinoNavigationBar的Widget。

/**

  • 脚手架
    */
    class PlatformScaffold
    extends BasePlatformWidget<Scaffold, CupertinoPageScaffold> {
    PlatformScaffold({this.appBar, this.body});

    final PlatformAppBar appBar;
    final Widget body;

    @override
    Scaffold createAndroidWidget(BuildContext context) {
    return Scaffold(
    appBar: appBar.createAndroidWidget(context),
    body: body,
    );
    }

    @override
    CupertinoPageScaffold createIosWidget(BuildContext context) {
    return CupertinoPageScaffold(
    navigationBar: appBar.createIosWidget(context),
    child: body,
    );
    }
    }

/**

  • AppBar
    */
    class PlatformAppBar
    extends BasePlatformWidget<AppBar, CupertinoNavigationBar> {
    final Widget title;
    final Widget leading;

    PlatformAppBar({this.title, this.leading});

    @override
    AppBar createAndroidWidget(BuildContext context) {
    return new AppBar(leading: leading, title: title,);
    }

    @override
    CupertinoNavigationBar createIosWidget(BuildContext context) {
    return new CupertinoNavigationBar(leading: leading, middle: title,);
    }

}

/**

  • 对话框
    */
    class PlatformAlertDialog
    extends BasePlatformWidget<AlertDialog, CupertinoAlertDialog> {

    final Widget title;
    final Widget content;
    final List actions;

    PlatformAlertDialog({this.title, this.content, this.actions});

    @override
    AlertDialog createAndroidWidget(BuildContext context) {
    return new AlertDialog(title: title, content: content, actions: actions,);
    }

    @override
    CupertinoAlertDialog createIosWidget(BuildContext context) {
    return new CupertinoAlertDialog(
    title: title, content: content, actions: actions,);
    }
    }

/**

  • Switch
    */

class PlatformSwicth extends BasePlatformWidget<Switch, CupertinoSwitch> {

final bool value;
final ValueChanged onChanged;

PlatformSwicth({this.value, this.onChanged});

@override
Switch createAndroidWidget(BuildContext context) {
return new Switch(value: value, onChanged: onChanged);
}

@override
CupertinoSwitch createIosWidget(BuildContext context) {
return new CupertinoSwitch(value: value, onChanged: onChanged);
}
}

/**

  • Button
    */
    class PlatformButton extends BasePlatformWidget<FlatButton, CupertinoButton> {
    final VoidCallback onPressed;
    final Widget child;

    PlatformButton({this.onPressed, this.child});

    @override
    FlatButton createAndroidWidget(BuildContext context) {
    return new FlatButton(onPressed: onPressed, child: child);
    }

    @override
    CupertinoButton createIosWidget(BuildContext context) {
    return new CupertinoButton(child: child, onPressed: onPressed);
    }
    }

运行实例

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: ‘Flutter Demo’,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: ‘Flutter Demo Home Page’),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
_MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State {
@override
void initState() {
super.initState();
}

bool androidSelected = false;
bool iosSelected = false;

@override
Widget build(BuildContext context) {
return new PlatformScaffold(
appBar: new PlatformAppBar(
title: new Text(widget.title),

  ),
  body: new SafeArea(child: new Container(
      child:
      new Column(
        children: [
          new CupertinoButton(
              child: new Text("显示ios风格的对话框"), onPressed: () {
            showDialog(context: context, builder: (context) {
              return new CupertinoAlertDialog(
                title: new Text("title"),
                content: new Text("content"),
                actions:
                [
                  new CupertinoButton(onPressed: () {},
                      child: new Text("Cancel")),
                  new CupertinoButton(onPressed: () {},
                      child: new Text("Confirm")),
                ]
                ,
              );
            });
          }),
          new FlatButton(child: new Text("显示android风格的对话框"), onPressed: () {
            showDialog(context: context, builder: (context) {
              return new AlertDialog(
                title: new Text("title"),
                content: new Text("content"),
                actions:
                [
                  new FlatButton(onPressed: () {},
                      child: new Text("Cancel")),
                  new FlatButton(onPressed: () {},
                      child: new Text("Confirm")),
                ]
                ,
              );
            });
          }),

//下面这段代码在ios平台运行会出错,在android平台可以运行,所以暂时注释掉,原因就是Switch需要有一个Material的祖先
// new Switch(
// value: androidSelected, onChanged: (value) {
// setState(() {
// androidSelected = value;
// });
// },),
new CupertinoSwitch(value: iosSelected, onChanged: (value) {
setState(() {
iosSelected = value;
});
}),

        ],
      )
  ),),
);

}
}

demo源码
https://github.com/LXD312569496/flutter-learing/blob/master/platform_widget_learning/lib/BasePlatformWidget.dart

发表评论

邮箱地址不会被公开。 必填项已用*标注