我正在学习 Flutter 的诀窍。 教程很棒,但是当我尝试用从列表生成的开关和导航栏替换硬编码的侧边栏开关和导航栏时,我遇到了一堆打字问题。基本上,我想由此生成它们:
var appPages = [
{
'name': 'Header 1',
'icon': const Icon(Icons.cloud_upload),
'content': const PlaceholderPage(
placeholderText: '<h1>Test Header 1</h1><br/>Lorem ipsum.')
},
{
'name': 'Header 2',
'icon': const Icon(Icons.delete_sweep_outlined),
'content': const PlaceholderPage(
placeholderText: '<h2>Test Header 2</h2><br/>Lorem ipsum.')
},
{
'name': 'Bold Italics',
'icon': const Icon(Icons.bloodtype),
'content': const PlaceholderPage(
placeholderText: '<i>Test <b>Bold</b> Italics</i><br/>Lorem ipsum.')
},
];
然后代替
Widget page;
switch (selectedIndex) {
case 0:
page = const PlaceholderPage(placeholderText: '<h1>Test Header 1</h1><br/>Lorem ipsum.');
break;
case 1:
page = const PlaceholderPage(placeholderText: '<h2>Test Header 2</h2><br/>Lorem ipsum.');
break;
case 2:
page = const PlaceholderPage(placeholderText: '<i>Test <b>Bold</b> Italics</i><br/>Lorem ipsum.');
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
我会:
var page = appPages[selectedIndex]['content'] as Widget?;
而不是列出每个 NavigationRailDestination:
destinations:
const [
NavigationRailDestination(
icon: Icon(Icons.cloud_upload),
label: Text('Header 1')),
NavigationRailDestination(
icon: Icon(Icons.delete_sweep_outlined),
label: Text('Header 2')),
NavigationRailDestination(
icon: Icon(Icons.bloodtype),
label: Text('Bold Italics')),
],
我想要这样的东西:
destinations:
appPages.map((e) => {
NavigationRailDestination(
icon: e['icon'] as Widget, label: Text(e['name'] as String))
}).toList() as List<NavigationRailDestination>
但目前这失败了
_TypeError (type 'List<Set<NavigationRailDestination>>' is not a subtype of type 'List<NavigationRailDestination>' in type cast)
有任何修复此错误的方法,或者更优雅的方法吗?
提前致谢!
pubspec.yaml:
name: sidebar_test
publish_to: 'none'
version: 0.1.0
environment:
sdk: '>=3.3.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0
material_design_icons_flutter: ^7.0.0
styled_text: ^8.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true
main.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:styled_text/styled_text.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
var appName = 'Sidebar Test';
var appTheme = ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0x006AAE3F)),
);
var appStyleTags = {
'h1': StyledTextTag(
style: TextStyle(
height: 2, fontSize: 50, color: appTheme.colorScheme.primary)),
'h2': StyledTextTag(
style: appTheme.textTheme.displayMedium!.copyWith(
color: appTheme.colorScheme.primary,
)),
'b': StyledTextTag(style: const TextStyle(fontWeight: FontWeight.bold)),
'i': StyledTextTag(style: const TextStyle(fontStyle: FontStyle.italic)),
};
var appPages = [
{
'name': 'Header 1',
'icon': const Icon(Icons.cloud_upload),
'content': const PlaceholderPage(
placeholderText: '<h1>Test Header 1</h1><br/>Lorem ipsum.')
},
{
'name': 'Header 2',
'icon': const Icon(Icons.delete_sweep_outlined),
'content': const PlaceholderPage(
placeholderText: '<h2>Test Header 2</h2><br/>Lorem ipsum.')
},
{
'name': 'Bold Italics',
'icon': const Icon(Icons.bloodtype),
'content': const PlaceholderPage(
placeholderText: '<i>Test <b>Bold</b> Italics</i><br/>Lorem ipsum.')
},
];
var appPagesFirst = appPages[0];
// var appPagesList = appPages.entries.toList();
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyAppState(),
child: MaterialApp(
title: appName,
theme: appTheme,
home: const HomePage(),
debugShowCheckedModeBanner: false,
restorationScopeId: appName,
),
);
}
}
class MyAppState extends ChangeNotifier {}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var selectedIndex = 0;
bool? navExtended;
@override
Widget build(BuildContext context) {
var colorScheme = Theme.of(context).colorScheme;
// Use appPages
var page = appPages[selectedIndex]['content'] as Widget?;
// Instead of switch:
// Widget page;
// switch (selectedIndex) {
// case 0:
// page = const PlaceholderPage(placeholderText: '<h1>Test Header 1</h1><br/>Lorem ipsum.');
// break;
// case 1:
// page = const PlaceholderPage(placeholderText: '<h2>Test Header 2</h2><br/>Lorem ipsum.');
// break;
// case 2:
// page = const PlaceholderPage(placeholderText: '<i>Test <b>Bold</b> Italics</i><br/>Lorem ipsum.');
// break;
// default:
// throw UnimplementedError('no widget for $selectedIndex');
// }
// The container for the current page, with its background color
// and subtle switching animation.
var mainArea = ColoredBox(
color: colorScheme.surfaceVariant,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: page,
),
);
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
return Row(
children: [
SafeArea(
child: NavigationRail(
selectedIndex: selectedIndex,
onDestinationSelected: (value) {
setState(() {
selectedIndex = value;
});
},
extended: navExtended != null
? navExtended ?? true
: constraints.maxWidth >= 600,
leading: StyledText(
text: '<b>$appName</b>',
tags: appStyleTags,
),
trailing: Expanded(
child: Align(
alignment: Alignment.bottomLeft,
child: IconButton(
icon: Icon((navExtended ?? true)
? MdiIcons.arrowCollapseLeft
: MdiIcons.arrowExpandRight),
onPressed: () {
setState(() {
setState(
() => navExtended = !(navExtended ?? true));
});
},
),
),
),
destinations:
// Use appPages
appPages.map((e) => {
NavigationRailDestination(
icon: e['icon'] as Widget, label: Text(e['name'] as String))
}).toList() as List<NavigationRailDestination>
// Instead of
// const [
// NavigationRailDestination(
// icon: Icon(Icons.cloud_upload),
// label: Text('Header 1')),
// NavigationRailDestination(
// icon: Icon(Icons.delete_sweep_outlined),
// label: Text('Header 2')),
// NavigationRailDestination(
// icon: Icon(Icons.bloodtype),
// label: Text('Bold Italics')),
// ],
),
),
Expanded(child: mainArea),
],
);
// }
},
),
);
}
}
class PlaceholderPage extends StatelessWidget {
const PlaceholderPage({
super.key,
required this.placeholderText,
});
final String placeholderText;
@override
Widget build(BuildContext context) {
return Column(children: [
const Spacer(),
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
child: StyledText(text: placeholderText, tags: appStyleTags))),
),
const Spacer(),
]);
}
}
Dart 的匿名函数是用括号和花括号定义的:
(s) { print(s); }
,但如果它由单个语句组成,你可以使用右箭头((s) => print(s);
,然后你必须省略花括号,否则事情会崩溃,或者更糟,被解释为一个集合。D'oh.
只需删除它们(或箭头)即可:
destinations:
appPages.map((e) =>
NavigationRailDestination(
icon: e['icon'] as Widget, label: Text(e['name'] as String))
).toList() as List<NavigationRailDestination>
工作沙箱:https://flutlab.io/editor/2041e217-a3b9-482f-bc3e-45c0a36895fd