一切皆組件的Flutter,安能辨我是雄雌


從一開始接觸Flutter,相信讀者都會銘記一句話,那就是——一切皆組件。今天我們就來體會一下這句話的神奇魔力,我們先從實際的產品需求說起。
我們先來看一個簡化的運行圖:

我們要實現如上圖所示的日期選擇器,App是iOS風格。
Flutter SDK自身有類似上圖的日期選擇器,但是Material Design的,於是我到Flutter庫中找到了一個名為flutter_date_pickers的三方庫,版本為0.1.4(https://pub.flutter-io.cn/packages/flutter_date_pickers)。
接下來就是集成這個庫了,具體代碼按照文檔直接復制:

@override
Widget build(BuildContext context) {
    DatePickerRangeStyles styles = DatePickerRangeStyles(
        selectedPeriodLastDecoration: BoxDecoration(
            color: Colors.red,
            borderRadius: BorderRadius.only(
                topRight: Radius.circular(10.0),
                bottomRight: Radius.circular(10.0))),
        selectedPeriodStartDecoration: BoxDecoration(
        color: Colors.green,
        borderRadius: BorderRadius.only(
            topLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0)),
        ),
        selectedPeriodMiddleDecoration:
            BoxDecoration(color: Colors.yellow, shape: BoxShape.rectangle),
    );
    return CupertinoPageScaffold(
                child: WeekPicker(
                    selectedDate: DateTime.now(),
                    onChanged: (dateRange) {},
                    firstDate: DateTime.now().subtract(Duration(days: 10)),
                    lastDate: DateTime.now().add(Duration(minutes: 10)),
                    datePickerStyles: styles)
    );
}

本來以為可以正常運行的,結果整個App崩潰了。報錯堆棧信息如下:

The following NoSuchMethodError was thrown building WeekPicker(dirty, dependencies: [_LocalizationsScope-[GlobalKey#678bc]]):
The getter 'firstDayOfWeekIndex' was called on null.
Receiver: null
Tried calling: firstDayOfWeekIndex

接着,根據堆棧信息找到代碼出錯位置,發現是這個庫中week_picker.dart文件中出現問題,下面的代碼是問題所在:

MaterialLocalizations localizations = MaterialLocalizations.of(context);

ISelectablePicker<DatePeriod> weekSelectablePicker = WeekSelectable(
    selectedDate,
    datePickerStyles.firstDayOfeWeekIndex ?? localizations.firstDayOfWeekIndex,
    firstDate,
    lastDate,
    selectableDayPredicate: selectableDayPredicate
);

很明顯,這里使用了MaterialLocalizations對象localizations,而MaterialLocalizations.of(context);方法返回了null,所以在接下來的代碼中拋出了空指針異常。
解決的方法很簡單,只要修改源碼,如果通過MaterialLocalizations來初始化localizations得到null,那么就通過CupertinoLocalizations來初始化它就行了。具體代碼如下:

CupertinoLocalizations localizations = CupertinoLocalizations.of(context);

在接下來的使用時,替換為:

localizations.datePickerDateOrder.index

即可。
但是,這畢竟需要改第三方庫的源代碼,有沒有辦法不改源碼呢?答案是肯定的。
我們一開始就提到一切皆組件的概念,那么,有沒有可能App依然使用iOS風格,然后把MaterialApp嵌套到CupertinoPageScaffold中呢?換一種說法,我們可不可以把MaterialApp和與之相關的Scaffold當做普通的組件,被CupertinoPageScaffold所包含呢?來看下面的代碼:

@override
Widget build(BuildContext context) {
DatePickerRangeStyles styles = DatePickerRangeStyles(
    selectedPeriodLastDecoration: BoxDecoration(
        color: Colors.red,
        borderRadius: BorderRadius.only(
            topRight: Radius.circular(10.0),
            bottomRight: Radius.circular(10.0))),
    selectedPeriodStartDecoration: BoxDecoration(
    color: Colors.green,
    borderRadius: BorderRadius.only(
        topLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0)),
    ),
    selectedPeriodMiddleDecoration:
        BoxDecoration(color: Colors.yellow, shape: BoxShape.rectangle),
);
return CupertinoPageScaffold(
    child: MaterialApp(
        home: Scaffold(
        	appBar: CupertinoNavigationBar(middle: Text('Incredible Flutter')),
            body: WeekPicker(
                selectedDate: DateTime.now(),
                onChanged: (dateRange) {},
                firstDate: DateTime.now().subtract(Duration(days: 10)),
                lastDate: DateTime.now().add(Duration(minutes: 10)),
                datePickerStyles: styles))));
}

仔細閱讀上述代碼,可見:我們只是在return語句中增加了MaterialApp和Scaffold組件,重新運行程序,結果可以正常運行了。
另外還要注意,Scaffold中的appBar,我們經常給定的是AppBar對象,但為了實現iOS的界面風格,我們將其值改為CupertinoNavigationBar,也是沒有問題的。
好了,本次分享到此結束,希望上述內容能夠幫到你。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM