flutter_platform_widgets 0.3.1

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 94

Flutter Platform Widgets

This project is an attempt to see if it is possible to create widgets that are platform aware. Currently in order to render targted Android or iOS device specific styles, you need to either conditionaly check the platform or create a set of widgets to render differently depending on the running platform.

Installation

Pubspec: https://pub.dartlang.org/packages/flutter_platform_widgets

Widgets

These set of widgets allow for rendering based on the target platform using a single cross platform set of widget.

alt text

alt text

Each PlatformWidget provides common properties directly as constructor arguments. If required further customization can be achieved by using the platform widget builder. See the Enhance section of each widget.

PlatformWidget

A widget that will render either the android widget or cupertino widget based on the target platform. The widgets themselves do not need to be specifically Material or Cupertino.

return PlatformWidget(
  ios: (_) => Icon(CupertinoIcons.flag),
  android: (_) => Icon(Icons.flag),
);

PlatformText

A widget that will render uppercase for Android. iOS will remain unchanged.

return PlatformText('Cancel');  

PlatformButton

A button that will render a RaisedButton for android or a CupertinoButton for iOS.

return PlatformButton(
  onPressed: () => print('send'),
  child: PlatformText('Send'),
);

Enhance

Extend with WidgetBuilder for android or iOS.

return PlatformButton(
  onPressed: () => print('send'),
  child: PlatformText('Send'),
  android: (_) => MaterialRaisedButtonData(...),
  ios: (_) => CupertinoButtonData(...)
);

PlatformIconButton

A clickable (tappable) button with an icon. Uses IconButton for android or CupertinoButton for ios.

return PlatformIconButton(
  onPressed: () => print('info pressed'),
  iosIcon: Icon(
    CupertinoIcons.info,
    size: 28.0,
  ),
  androidIcon: Icon(Icons.info)
);

Enhance

Extend with WidgetBuilder for android or iOS.

Widget infoIconButton() {
  return PlatformIconButton(
    onPressed: () => print('info pressed'),
    iosIcon: Icon(CupertinoIcons.info),
    androidIcon: Icon(Icons.info),
    android: (_) => MaterialIconButtonData(...),
    ios: (_) => CupertinoIconButtonData(...),
  );
}

PlatformScaffold

A Scaffold that provides the correctly hosted header (AppBar) and navigation bar (Bottom Bar) for each platform. Uses Scaffold for android or CupertinoTabScaffold for ios with bottom tabs or CupertinoPageScaffold for ios without bottom tabs.

return PlatformScaffold(
  appBar: PlatformAppBar()
  body: _buildContent(),
  bottomNavBar: PlatformNavBar(),
);

Enhance

Extend with WidgetBuilder for android or iOS.

return PlatformScaffold(
  appBar: PlatformAppBar()
  body: _buildContent(),
  bottomNavBar: PlatformNavBar(),
  android: (_) => MaterialScaffoldData(...)
  ios: (_) => CupertinoScaffoldData(...);
);

PlatformAppBar

The AppBar is the top Header bar with a title, leftside or rightside buttons. Uses AppBar for android or CupertinoNavigationBar for ios.

return PlatformAppBar(
    title: new Text('Platform Widgets'),
    leading: PlatformIconButton()),
    trailingActions: <Widget>[
      PlatformIconButton(),
    ],
  );

Enhance

Extend with WidgetBuilder for android or iOS.

return PlatformAppBar(
  title: new Text('Platform Widgets'),
  leading: PlatformIconButton()),
  trailingActions: <Widget>[
    PlatformIconButton(),
  ],
  android: (_) => MaterialAppBarData(...),
  ios: (_)=> CupertinoNavigationBarData(...),
);

PlatformNavBar

Note: hasNotch has been removed to allow for the widget to work with the change on the development branch of flutter. To work around the breaking change either use the Material BottomAppBar directly or cast the result from PlatformNavBar to BottomAppBar for android builds and set the hasNotch property. Otherwise target version 0.2.0

The NavBar is placed at the bottom of the page with a set of buttons that typically navigate between screens. Implementing this widget requires the parent widget to manage the currentIndex of the page and to set PlatformNavBar.currrentIndex. Uses BottomAppBar with BottomNavigationBar for android or CupertinoTabBar for ios.

return PlatformNavBar(
  currentIndex: _selectedTabIndex,
  itemChanged: (index) => setState(
        () {
          _selectedTabIndex = index;
        },
      ),
  items: [
    BottomNavigationBarItem(),
    BottomNavigationBarItem(),
  ],
);

Enhance

Extend with WidgetBuilder for android or iOS.

return PlatformNavBar(
  currentIndex: _selectedTabIndex,
  itemChanged: (index) => setState(
        () {
          _selectedTabIndex = index;
        },
      ),
  items: [
    BottomNavigationBarItem(),
    BottomNavigationBarItem(),
  ],
  android: (_) => MaterialNavBarData(...);
  ios: (_) => CupertinoTabBarData(...),
);

PlatformAlertDialog

The AlertDialog will render a caption/title, body/text and a set of action buttons specific for the platform. Uses AlertDialog for android or CupertinoAlertDialog for ios.

Note that showDialog from the material package needs to be used to make it easy to render.

alt text

alt text

showDialog(
  context: context,
  builder: (_) => PlatformAlertDialog(
    title: Text('Alert'),
    content: Text('Some content'),
    actions: <Widget>[
      PlatformDialogAction(),
      PlatformDialogAction(),
    ],
  ),
);

Enhance

Extend with WidgetBuilder for android or iOS.

showDialog(
  context: context,
  builder: (_) => PlatformAlertDialog(...);
  ios: (_) => CupertinoAlertDialogData(...),
  android: (_) => MaterialAlertDialogData(...),
)

PlatformDialogAction

The DialogAction widget is used to describe the set of buttons on the AlertDialog. Uses FlatButton for android or CupertinoDialogAction for ios.

PlatformDialogAction(
  child: PlatformText('Cancel'),
  onPressed: () => Navigator.pop(context),
),

Enhance

Extend with WidgetBuilder for android or iOS.

PlatformDialogAction(
  child: PlatformText('Cancel'),
  onPressed: () => Navigator.pop(context),
  android: (_) => MaterialDialogActionData(...),
  ios: (_) => CupertinoDialogActionData(...),
),

PlatformCircularProgressIndicator

A circular looking progress indicator. Uses CircularProgressIndicator for android or CupertinoActivityIndicator for ios.

return PlatformCircularProgressIndicator();

Enhance

Extend with WidgetBuilder for android or iOS.

return PlatformCircularProgressIndicator(
  android: (_) => MaterialProgressIndicatorData(...),
  ios: (_)=> CupertinoProgressIndicatorData(...),
);

TODO

  • UI / Unit Tests.
  • Code documentation

The following are a list more platform aware widgets needing to be added.

Changing / Checking Platform

When importing flutter_platform_widgets you can check isMaterial or isCupertino to determine what style will be used. This is independent to Platform.isAndroid or Platform.isIOS from 'import 'dart:io'

See the example code for how this is used.

Known Limitations

Best to set to fixed if the number of navigation items are 4 or more.

return PlatformNavBar(
   android: (_) => MaterialNavBarData(
      type: BottomNavigationBarType.fixed,
    ),
  • Setting BottomNavigationBar.fixedColor to anything has no effect.

  • If using the Cupertino widgets it may complain that there is no Material parent when using material widgets further doen the widget tree. If this is the case you need to place Material as a child widget

return PlatformScaffold(
  body: Material(
    color: Colors.white,
    child: _theBodyOfThePageWithMaterialWidgets(),
  );
);

Note: You may fine without setting the color of the Material widget there will be a slight grey color appear as the background. You may need to explicitly set the color to match the rest of the page

  • Cupertino widgets do not pick up the Theme in all cases. In particular the Text() widget needs to have a DefaultTheme set otherwise all Text() widgets need to have their style property set.
return DefaultTextStyle(
  style: Theme.of(context).textTheme.body1,
  child: Center(
     child: Column(
       children: <Widget>[
         PlatformText('Text 1'),
         PlatformText('Text 2'),
         PlatformText('Text 3'),
       ],
     ),
   ),
 );

Issues and Feedback

Please create an issue to provide feedback or an issue.

Acknowledgements

Inspired by the example given by Swav Kulinski (https://github.com/swavkulinski/flutter-platform-specific-widgets)

[0.3.1] - Aug 24, 2018

  • Updated environment to support Dart 2

[0.3.0] - Aug 13, 2018

  • Removed hasNotch to be compatible with the flutter develop branch

[0.2.0] - May 16, 2018

  • Added CircularIndicator

[0.1.1 - 0.1.4]

  • Bug fixes

[0.1.0] - May 11, 2018

  • Inital Release

example/example.dart

/*
 * flutter_platform_widgets
 * Copyright (c) 2018 Lance Johnstone. All rights reserved.
 * See LICENSE for distribution and usage details.
 */

import 'package:flutter/widgets.dart';

import 'package:flutter/material.dart'
    show
        showDialog,
        Theme,
        DefaultTextStyle,
        Colors,
        FloatingActionButton,
        BottomNavigationBarType,
        MaterialPageRoute,
        Icons;

import 'package:flutter/cupertino.dart' show CupertinoIcons, CupertinoPageRoute;

import '../lib/flutter_platform_widgets.dart';

import 'sub_pages.dart';

class ExamplePage extends StatefulWidget {
  ExamplePage({Key key}) : super(key: key);

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

class _ExamplePageState extends State<ExamplePage> {
  int _selectedTabIndex = 0;

  void _showSubPage() {

//need for ios different navigation stacks so the bottom tab bar does not hide
//https://stackoverflow.com/questions/46502751/how-to-use-multiple-navigators
//https://stackoverflow.com/questions/45511549/permanent-view-with-navigation-bar-in-flutter
//https://github.com/flutter/flutter/blob/9909e773dc66608a866efa7388f39127509d0e1e/packages/flutter/lib/src/cupertino/tab_view.dart
//https://stackoverflow.com/questions/46483949/how-to-get-current-route-path-in-flutter

    if (isMaterial) {
      Navigator.push(context, MaterialPageRoute(builder: (_) => SubPage1()));
    } else {
      Navigator.push(context, CupertinoPageRoute(builder: (_) => SubPage1()));
    }
  }

  void showExampleAlertDialog() {
    showDialog(
        context: context,
        builder: (_) => PlatformAlertDialog(
              title: Text('Alert'),
              content: Text('Some content'),
              actions: <Widget>[
                PlatformDialogAction(
                  android: (_) => MaterialDialogActionData(),
                  ios: (_) => CupertinoDialogActionData(),
                  child: PlatformText('Cancel'),
                  onPressed: () => Navigator.pop(context),
                ),
                PlatformDialogAction(
                  child: PlatformText('OK'),
                  onPressed: () => Navigator.pop(context),
                ),
              ],
            ));
  }

  Widget _buildContent() {
    return DefaultTextStyle(
      style: Theme.of(context).textTheme.body1,
      child: Center(
        child: Column(
          children: <Widget>[
            new Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text(
                'Standard Text',
              ),
            ),
            new Padding(
              padding: const EdgeInsets.all(8.0),
              child: PlatformText(
                'Platform Text - (uppercase in Android)',
              ),
            ),
            new Padding(
              padding: const EdgeInsets.all(8.0),
              child: PlatformButton(
                onPressed: () => showExampleAlertDialog(),
                child: PlatformText('Show Alert Dialog'),
              ),
            ),
            new Padding(
              padding: const EdgeInsets.all(8.0),
              child: PlatformButton(
                onPressed: () => setState(() => changeToMaterialPlatform()),
                child: PlatformText('Switch to Material'),
              ),
            ),
            new Padding(
              padding: const EdgeInsets.all(8.0),
              child: PlatformButton(
                onPressed: () => setState(() => changeToCupertinoPlatform()),
                child: Text('Switch to Cupertino'),
              ),
            ),
            new Padding(
              padding: const EdgeInsets.all(8.0),
              child: PlatformButton(
                onPressed: () => _showSubPage(),
                child: Text('Display Sub Page'),
              ),
            ),
            new Padding(
              padding: const EdgeInsets.all(8.0),
              child: PlatformCircularProgressIndicator(
                ios: (_) => CupertinoProgressIndicatorData(radius: 16.0),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildBottomNavBar() {
    return PlatformNavBar(
      currentIndex: _selectedTabIndex,
      itemChanged: (index) => setState(
            () {
              _selectedTabIndex = index;
            },
          ),
      android: (_) => MaterialNavBarData(
            backgroundColor: Colors.lightBlue,
            type: BottomNavigationBarType.fixed,
          ),
      items: [
        BottomNavigationBarItem(
          title: Text('Flag'),
          icon: PlatformWidget(
            ios: (_) => Icon(
                  CupertinoIcons.flag,
                ),
            android: (_) => Icon(Icons.flag),
          ),
        ),
        BottomNavigationBarItem(
          title: Text('Book'),
          icon: PlatformWidget(
            ios: (_) => Icon(CupertinoIcons.book),
            android: (_) => Icon(Icons.book),
          ),
        ),
        BottomNavigationBarItem(
          title: Text('Phone'),
          icon: PlatformWidget(
            ios: (_) => Icon(CupertinoIcons.phone),
            android: (_) => Icon(Icons.phone),
          ),
        ),
        BottomNavigationBarItem(
          title: Text('Home'),
          icon: PlatformWidget(
            ios: (_) => Icon(CupertinoIcons.home),
            android: (_) => Icon(Icons.home),
          ),
        ),
      ],
    );
  }

  Widget _buildAppBar() {
    return PlatformAppBar(
      title: new Text(
        'Platform Widgets',
      ),
      leading: PlatformIconButton(
        onPressed: () {},
        iosIcon: Icon(
          CupertinoIcons.info,
          size: 28.0,
          color: Theme.of(context).primaryColor,
        ),
        androidIcon: Icon(Icons.info),
      ),
      trailingActions: <Widget>[
        PlatformIconButton(
          ios: (_) => CupertinoIconButtonData(
                onPressed: () {
                  print('ios onpressed');
                },
              ),
          onPressed: () {
            print('onpressed');
          },
          iosIcon: Icon(
            CupertinoIcons.share,
            size: 28.0,
          ),
          androidIcon: Icon(Icons.share),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return PlatformScaffold(
      appBar: _buildAppBar(),
      body: _buildContent(),
      bottomNavBar: _buildBottomNavBar(),
      android: (_) => MaterialScaffoldData(
            floatingActionButton: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {},
            ),
          ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_platform_widgets: ^0.3.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
  
Version Uploaded Documentation Archive
0.3.1 Aug 24, 2018 Go to the documentation of flutter_platform_widgets 0.3.1 Download flutter_platform_widgets 0.3.1 archive
0.3.0 Aug 13, 2018 Go to the documentation of flutter_platform_widgets 0.3.0 Download flutter_platform_widgets 0.3.0 archive
0.2.0 May 16, 2018 Go to the documentation of flutter_platform_widgets 0.2.0 Download flutter_platform_widgets 0.2.0 archive
0.1.4 May 15, 2018 Go to the documentation of flutter_platform_widgets 0.1.4 Download flutter_platform_widgets 0.1.4 archive
0.1.3 May 15, 2018 Go to the documentation of flutter_platform_widgets 0.1.3 Download flutter_platform_widgets 0.1.3 archive
0.1.2 May 14, 2018 Go to the documentation of flutter_platform_widgets 0.1.2 Download flutter_platform_widgets 0.1.2 archive
0.1.1 May 14, 2018 Go to the documentation of flutter_platform_widgets 0.1.1 Download flutter_platform_widgets 0.1.1 archive
0.1.0 May 11, 2018 Go to the documentation of flutter_platform_widgets 0.1.0 Download flutter_platform_widgets 0.1.0 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
88
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
94
Learn more about scoring.

We analyzed this package on Sep 18, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0
  • pana: 0.12.3
  • Flutter: 0.8.4

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.23.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11
meta 1.1.6
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test