最近在学flutter 以下以web前端视角对比看待
教程
codelabs.developers.google.com/codelabs/fl...
效果

代码
js
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
import 'package:provider/provider.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return ChangeNotifierProvider(
create: (context)=>MyAppState(),
child:MaterialApp(
title: "Namer App",
theme:ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green)
),
home: MyHomePage()
)
);
}
}
class MyAppState extends ChangeNotifier{
var current = WordPair.random();
void getNext(){
current = WordPair.random();
notifyListeners();
}
var favorites = <WordPair>[];
void toggleFavorite(){
if(favorites.contains(current)){
favorites.remove(current);
}else{
favorites.add(current);
}
notifyListeners();
}
}
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var selectedIndex = 1;
var selectedIndexInAnotherWidget =0;
var indexInYetAnotherWidget = 42;
var optionASelected = false;
var optionBSelected = false;
var loadingFromNetwork = false;
@override
Widget build(BuildContext context) {
Widget page;
switch (selectedIndex){
case 0:
page = GeneratorPage();
break;
case 1:
page = FavoritesPage();
break;
default:
throw UnimplementedError('no widget for $selectedIndex');
}
return LayoutBuilder(
builder: (context, constraints) {
return Scaffold(
body:Row(
children: [
Expanded(
child: SafeArea(
child: NavigationRail(
extended: constraints.maxWidth>=600,
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home')
),
NavigationRailDestination(
icon: Icon(Icons.favorite),
label: Text('Favorites')
),
],
selectedIndex:selectedIndex,
onDestinationSelected: (value){
setState(() {
selectedIndex = value;
});
},
),
),
),
Expanded(
child: Container(
color:Theme.of(context).colorScheme.primaryContainer,
child:page,
)
)
],
)
);
}
);
}
}
class GeneratorPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
var pair = appState.current;
IconData icon;
if(appState.favorites.contains(pair)){
icon = Icons.favorite;
}else{
icon = Icons.favorite_border;
}
return Center(
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BigCard(pair: pair),
SizedBox(
height: 10,
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton.icon(
onPressed: (){
appState.toggleFavorite();
},
icon:Icon(icon),
label: Text('Like')
),
SizedBox(width: 10,),
ElevatedButton(
onPressed: (){
appState.getNext();
},
child: Text('Next'),
)
],
)
],
)
);
}
}
class BigCard extends StatelessWidget {
const BigCard({
super.key,
required this.pair,
});
final WordPair pair;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final style = theme.textTheme.displayMedium!.copyWith(
color:theme.colorScheme.onPrimary
);
return Card(
color: theme.colorScheme.primary,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Text(
pair.asLowerCase,
style:style,
semanticsLabel: "${pair.first} ${pair.second}",
),
),
);
}
}
class FavoritesPage extends StatefulWidget {
@override
State<FavoritesPage> createState() => _FavoritesPageState();
}
class _FavoritesPageState extends State<FavoritesPage> {
@override
Widget build(BuildContext context) {
var appState = context.watch<MyAppState>();
if(appState.favorites.isEmpty){
return Center(
child: Text('No favorites yet.'),
);
}
return ListView(
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Text('You have '
'${appState.favorites.length} favorites:'),
),
for (var pair in appState.favorites)
ListTile(
leading: Icon(Icons.favorite),
title: Text(pair.asLowerCase),
)
],
);
}
}
功能
- 启动 flutter run
package script vue-cli vite webpack dev-server - 入口 runApp
createRoot new Vue createApp 没有css html xml dsl形式 没有mount - app StatelessWidget或者StatefulWidget
类组件 React.component h - render Widget build(BuildContext context) {
setup render template - 组件 结构样式逻辑一切皆组件 混在一起 没有css html xml dsl形式
html css js ts
vue template script style
react jsx tsx className style - 状态管理
ChangeNotifierProvider ChangeNotifier->notifyListeners context.watch
react context 纯传递 functionCompnent定义数据与方法再传递进去
createContext context.Provier setState useContext vue inject provide 纯传递 pinia defineStore 直接引用 react类组件与flutter类似 要手动触发更新 notifyListeners setState 手动挡
vue响应式不需要传递修改值即可 自动挡 - 组件库
官方根据android与ios风格 Material与cupertino 平台->官方 二选一 vue react ui库 自行找组件库 自由选择
组件
- StatelessWidget
- ChangeNotifierProvider ChangeNotifier
- MaterialApp ThemeData
- StatefulWidget State
- LayoutBuilder
- Scaffold SafeArea
- Container Row Column Expanded Center SizedBox Padding
- NavigationRail NavigationRailDestination
- ElevatedButton Icon Card Text
- ListView ListTile
思想
一切皆组件无限嵌套
组件多级封装 组件语法糖 比如 SizedBox->Container Column|Row->Center
组件更新
1.ChangeNotifier->notifyListeners();
2.StatefulWidget->setState
第一反应
语言感觉不上不下 优点 语法糖 比如具名参数 空判断 assert性能优化 官方主导统一库与工具链 缺点 落后写法 比如类型后置 没有dsl 嵌套太多 太多分层组件 面向对象太多继承 不太简洁