flutter写后感 构建您的第一个 Flutter 应用

160 阅读2分钟

最近在学flutter 以下以web前端视角对比看待

教程

codelabs.developers.google.com/codelabs/fl…

效果

image.png

代码

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),
          )
      ],
    );
  }
}

功能

  1. 启动 flutter run
    package script vue-cli vite webpack dev-server
  2. 入口 runApp
    createRoot new Vue createApp 没有css html xml dsl形式 没有mount
  3. app StatelessWidget或者StatefulWidget
    类组件 React.component h
  4. render Widget build(BuildContext context) {
    setup render template
  5. 组件 结构样式逻辑一切皆组件 混在一起 没有css html xml dsl形式
    html css js ts
    vue template script style
    react jsx tsx className style
  6. 状态管理
    ChangeNotifierProvider ChangeNotifier->notifyListeners context.watch
    react context 纯传递 functionCompnent定义数据与方法再传递进去
    createContext context.Provier setState useContext vue inject provide 纯传递 pinia defineStore 直接引用 react类组件与flutter类似 要手动触发更新 notifyListeners setState 手动挡
    vue响应式不需要传递修改值即可 自动挡
  7. 组件库
    官方根据android与ios风格 Material与cupertino 平台->官方 二选一 vue react ui库 自行找组件库 自由选择

组件

  1. StatelessWidget
  2. ChangeNotifierProvider ChangeNotifier
  3. MaterialApp ThemeData
  4. StatefulWidget State
  5. LayoutBuilder
  6. Scaffold SafeArea
  7. Container Row Column Expanded Center SizedBox Padding
  8. NavigationRail NavigationRailDestination
  9. ElevatedButton Icon Card Text
  10. ListView ListTile

思想

一切皆组件无限嵌套
组件多级封装 组件语法糖 比如 SizedBox->Container Column|Row->Center
组件更新
1.ChangeNotifier->notifyListeners();
2.StatefulWidget->setState

第一反应

语言感觉不上不下 优点 语法糖 比如具名参数 空判断 assert性能优化 官方主导统一库与工具链 缺点 落后写法 比如类型后置 没有dsl 嵌套太多 太多分层组件 面向对象太多继承 不太简洁