Flutter For Web入門實戰

Google在今年5月的Google大會上發佈了Flutter1.5.4版本,同時也推出了Flutter for Web的預覽版,並開啓了Flutter的全棧框架之路。同時,今年9月舉行的谷歌開發者大會上,Google宣佈flutter1.9正式發佈,而且flutter_web已經被合到master分支,說明flutter_web愈來愈受到Google的重視。web

首先切換到master並升級flutter到最新版本,或者下載最新的Stable channel版本,使用命令方式升級的命令以下:macos

flutter channel master
flutter upgrade
複製代碼

默認狀況下,flutter_web是沒有啓動的,須要開發者手動啓動它,啓動的命令以下:bash

flutter pub global activate webdev
複製代碼

運行上面的命令可能會提示須要添加環境變量,以下所示:app

在這裏插入圖片描述
按照提示,打開~ > .bash_profile文件把

export PATH="$PATH":"$HOME/Flutter/flutter/.pub-cache/bin"
複製代碼

添加進去,而後使用source ~/.bash_profile命令更新環境變量。到這webdev就完事了,命令行敲webdev測試一下,若是沒有任何錯誤,會看到以下幫助信息。框架

在這裏插入圖片描述
須要說明的是,若是沒有用flutter自帶的dart-sdk而是單獨安裝,這裏可能會由於dart版本與flutter版本不匹配而出現以下提示。

Can't load Kernel binary: Invalid kernel binary format version. No active package webdev. 複製代碼

出現這種狀況須要先把dart卸載,而後如前邊所述將flutter內置的dart-sdk添加到環境變量就能夠了,卸載的命令以下:less

brew uninstall dart
複製代碼

而後,使用以下的命令啓動flutter_web。iphone

flutter config --enable-web
複製代碼

出現以下提示,說明咱們尚未建立項目。ide

Setting "enable-web" value to "true".
複製代碼

若是是最新的1.9.0及其以上版本,只須要將分支切換到master便可,切換的命令以下:工具

flutter channel master       //切換到master分支
複製代碼

接下來,就能夠使用命令行或者Android Studio、VSCode等可視化工具來建立Flutter Web應用了,以下圖所示。 測試

在這裏插入圖片描述
能夠發現,新建的Flutter Web項目比原來的項目會多兩個包,即web包和macOS包。要運行Flutter Web應用或者桌面,只須要點擊工具欄上對應的設備便可,以下圖所示。
在這裏插入圖片描述
選擇運行環境爲Chrome(web),而後運行Flutter Web示例項目,最終效果以下圖所示。
在這裏插入圖片描述
當選擇運行的環境爲桌面時,系統運行時就會啓動一個桌面應用,以下圖所示。
在這裏插入圖片描述

接下來,咱們修改下默認的示例項目來看下Flutter在桌面和Web的狀況。例以下面是仿網易雲音樂的登陸界面,示例代碼以下: login_page.dart代碼

import 'package:flutter/material.dart';
import 'package:flutter_desk/widgets/common_button.dart';
import 'package:flutter_desk/widgets/v_empty_view.dart';

class LoginPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _LoginPageState();
  }
}

class _LoginPageState extends State<LoginPage> with TickerProviderStateMixin {

  Animation<double> _animation;
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    _animation = CurvedAnimation(parent: _controller, curve: Curves.linear);
    Future.delayed(Duration(milliseconds: 500), () {
      _controller.forward();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        brightness: Brightness.light,
      ),
      body: SingleChildScrollView(
        child: Container(
          padding: EdgeInsets.only(
            left: 80,
            right:80,
            top:30,
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Hero(
                tag: 'logo',
                child: Image.asset(
                  'images/icon_logo.png',
                  width: 90,
                  height: 90,
                ),
              ),
              _LoginAnimatedWidget(
                animation: _animation,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _LoginWidget extends StatefulWidget {
  @override
  _LoginWidgetState createState() => _LoginWidgetState();
}

class _LoginWidgetState extends State<_LoginWidget> {
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _pwdController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Theme(
      data: ThemeData(primaryColor: Colors.red),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Container(
            margin: EdgeInsets.only(top: 30),
            child: Text(
              'Welcome Back!',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.black87,
                fontSize: 34,
              ),
            ),
          ),
          Container(
            margin: EdgeInsets.only(top:3),
            child: Text(
              'The Flutter Netease Cloud Music App',
              style: TextStyle(
                color: Colors.grey,
                fontSize: 14,
              ),
            ),
          ),
          VEmptyView(50),
          TextField(
            controller: _phoneController,
            decoration: InputDecoration(
                hintText: 'Phone',
                prefixIcon: Icon(
                  Icons.phone_iphone,
                  color: Colors.grey,
                )),
          ),
          VEmptyView(40),
          TextField(
            obscureText: true,
            controller: _pwdController,
            decoration: InputDecoration(
                hintText: 'Password',
                prefixIcon: Icon(
                  Icons.lock,
                  color: Colors.grey,
                )),
          ),
          VEmptyView(120),
          CommonButton(
            callback: () {
              String phone = _phoneController.text;
              String pwd = _pwdController.text;
              if (phone.isEmpty || pwd.isEmpty) {
                return;
              }
            },
            content: 'Login',
            width: double.infinity,
          )
        ],
      ),
    );
  }
}

class _LoginAnimatedWidget extends AnimatedWidget {
  final Tween<double> _opacityTween = Tween(begin: 0, end: 1);
  final Tween<double> _offsetTween = Tween(begin: 40, end: 0);
  final Animation animation;

  _LoginAnimatedWidget({
    @required this.animation,
  }) : super(listenable: animation);

  @override
  Widget build(BuildContext context) {
    return Opacity(
      opacity: _opacityTween.evaluate(animation),
      child: Container(
        margin: EdgeInsets.only(top: _offsetTween.evaluate(animation)),
        child: _LoginWidget(),
      ),
    );
  }
}

複製代碼

common_button.dart代碼

import 'package:flutter/material.dart';

class CommonButton extends StatelessWidget {

  final VoidCallback callback;
  final String content;
  final double width;
  final double height;
  final double fontSize;

  CommonButton({
    @required this.callback,
    @required this.content,
    this.width = 250,
    this.height = 50,
    this.fontSize = 18,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      width: width,
      height: height,
      child: RaisedButton(
        onPressed: callback,
        color: Colors.red,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(height / 2))),
        child: Text(
          content,
          style: TextStyle(color: Colors.white, fontSize: fontSize),
        ),
      ),
    );
  }
}

複製代碼

empty_view.dart代碼

import 'package:flutter/material.dart';

class VEmptyView extends StatelessWidget {

  final double height;

  VEmptyView(this.height);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: height,
    );
  }
}

複製代碼

而後,分別將運行環境改成Chorme和MacOS桌面,便可看到對應的效果,以下圖所示。

在這裏插入圖片描述

相關文章
相關標籤/搜索