Flutter跨平台导航栏适配:PreferredSizeWidget类型转换报错求解
我想把Material风格的AppBar和Cupertino风格的CupertinoNavigationBar统一存储到一个变量中,方便后续Widget通过preferredSize属性计算尺寸。但将变量类型声明为PreferredSizeWidget时,出现以下错误:
A value of type 'Widget' can't be assigned to a variable of type 'PreferredSizeWidget'.
Try changing the type of the variable, or casting the right-hand type to 'PreferredSizeWidget'.
如何对右侧值进行类型转换,同时保证跨平台适配正常且能正确使用preferredSize属性?以下是我的build方法代码:
@override Widget build(BuildContext context) { final mediaQuery = MediaQuery.of(context); final isLandscape = mediaQuery.orientation == Orientation.landscape; // ERROR CAUSE BY THIS VARIABLE final PreferredSizeWidget appBar = Platform.isIOS? CupertinoNavigationBar() : AppBar( title: Text('Expense Planner'), actions: [ IconButton( onPressed: () => startNewTransactionInput(context), icon: Icon( Icons.add, color: Colors.white, ), ) ], ); final listWidget = SizedBox( height: (mediaQuery.size.height - mediaQuery.padding.top - appBar.preferredSize.height) * 0.75, child: TransactionList(_userTransactionList, _deleteTransaction), ); final pageBody = SingleChildScrollView( child: Column( // mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (isLandscape) Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Show Chart'), // it abjust styling of the switch based on device palform Switch.adaptive( value: _showChart, onChanged: (val) { setState(() { _showChart = val; }); }) ], ), // weakly expense chart if (!isLandscape) SizedBox( height: (mediaQuery.size.height - mediaQuery.padding.top - appBar.preferredSize.height) * 0.25, child: Chart(_recentTransactions), ), if (!isLandscape) listWidget, if (isLandscape) _showChart ? SizedBox( height: (mediaQuery.size.height - mediaQuery.padding.top - appBar.preferredSize.height) * 0.7, child: Chart(_recentTransactions), ) : listWidget, ], ), ); return Platform.isIOS ? CupertinoPageScaffold( child: pageBody, navigationBar: appBar, // insted of appbar here nevigationBart ) : Scaffold( // title of the app appBar: appBar, // body of the app body: pageBody, floatingActionButton: Platform.isIOS ? Container() : FloatingActionButton( child: Icon(Icons.add), onPressed: () { startNewTransactionInput(context); }, ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, ); }
解决方案
问题根源是Dart的类型推断机制:三元运算符的两个分支CupertinoNavigationBar和AppBar虽然都实现了PreferredSizeWidget接口,但它们的公共父类是Widget,所以整个表达式的类型会被推断为Widget,无法直接赋值给PreferredSizeWidget类型的变量。
你可以通过以下两种方式解决:
方式1:对每个分支单独转换类型
final PreferredSizeWidget appBar = Platform.isIOS ? CupertinoNavigationBar() as PreferredSizeWidget : AppBar( title: Text('Expense Planner'), actions: [ IconButton( onPressed: () => startNewTransactionInput(context), icon: Icon( Icons.add, color: Colors.white, ), ) ], ) as PreferredSizeWidget;
方式2:对整个三元表达式强制转换
final PreferredSizeWidget appBar = (Platform.isIOS ? CupertinoNavigationBar() : AppBar( title: Text('Expense Planner'), actions: [ IconButton( onPressed: () => startNewTransactionInput(context), icon: Icon( Icons.add, color: Colors.white, ), ) ], )) as PreferredSizeWidget;
修改后,变量appBar会被正确识别为PreferredSizeWidget类型,既可以正常调用preferredSize.height计算尺寸,也能分别传入CupertinoPageScaffold的navigationBar参数和Scaffold的appBar参数,完全适配跨平台场景。
内容的提问来源于stack exchange,提问作者Smit Prajapati

