Taste of

web 

with

Flutter

Majid Hajian

mhadaily

Updated - Flutter2

Credit to: JDominik Roszkowski - https://codepen.io/orestesgaolin/pen/ExVboMY

https://chrome-trex-flutter.netlify.app/#/

Credit to: Joshua de Guzman - https://codepen.io/joshuadeguzman/pen/jObrzJB

https://nike-shop-flutter.netlify.app

Credit to: Zoey Fan - https://codepen.io/zoeyfan/pen/ExVaXGK

https://gooey-edge-flutter.netlify.app

mhadaily

mhadaily

Agenda

  • Dart vs JS / TS
  • Flutter vs Web / Web vs Flutter?
  • How Flutter web works!
  • How / Where to start?
  • DEMO

ME.dart

import 'package:flutter/material.dart';
MaterialApp(
   ThemeData(
        name: "Majid Hajian",
        location: "Oslo, Norway",
        description: '''
                Google Developer Expert
        	Passionate Software engineer, 
	        Community Leader, Author and international Speaker
         ''',
        main: "Flutter/Dart, PWA, Performance",
        homepage: "https://www.majidhajian.com",
        socials: {
          twitter: "https://www.twitter.com/mhadaily",
          github: "https://www.github.com/mhadaily"
        },
        author: {
          Pluralsight: "www.pluralsight.com/authors/majid-hajian",
          Apress: "Progressive Web App with Angular, Book",
          PacktPub: "PWA development",
          Udemy: "PWA development",
        }
        founder: "Softiware As (www.Softiware.com)"
        devDependencies: {
          tea: "Ginger", 
          mac: "10.14+",
        },
        community: {
          MobileEraConference: "Orginizer",
          FlutterVikings: "Orginizer", 
          FlutterDartOslo: "Orginizer",
          GDGOslo: "Co-Orginizer",
          DevFestNorway: "Orginizer",
          ...more
        }));

mhadaily

Find me on the internet by

  • Compiled Natively
  • Single Codebase

Mobile / Desktop / Web / Embedded

  • Google's UI Toolkit
  • Only Dart!

Expressive & Flexible UI

Fast Development

Native Performance

mhadaily

Expressive & Flexible UI

Fast Development

Native Performance

Productive

mhadaily

mhadaily

mhadaily

https://skia.org/

Skia is an open-source 2D graphics library that provides common APIs that work across a variety of hardware and software platforms.

mhadaily

mhadaily

Dart to JS

https://dart.dev/tools/dart2js

 dart2js -O2 -o test.js test.dart

mhadaily

Web Renderers

HTML renderer

Uses a combination of HTML elements, CSS, Canvas elements, and SVG elements. This renderer has a smaller download size.


CanvasKit renderer

This renderer is fully consistent with Flutter mobile and desktop, has faster performance with higher widget density, but adds about 2MB in download size.

WebAssembly and Skia

mhadaily

Scenarios

A PWA built with Flutter

Single Page Application

Existing Mobile Applications

At this time

mhadaily

Why Dart?

  • Easy to learn
  • Familiarity to users with both static and dynamic languages

mhadaily

Similarities

  • Event Loop
  • Just-in-time compilation
  • Ahead-of-time complication
  • Type safe
class User {
  id: number;
	
  constructor(public name: string){id=1};
    
    
   async fetchInfo() {
     return new Promise(
       resolve => setTimeout(resolve, 3*1000);
     );
   }
}


async function main(){

  const user = new User('Majid');
  
  await user.fetchInfo();
  
  console.log(`Hello ${user.id}`)
}
main();
class User {
  final int id;
  final String name;
	
  User(this.name) {id = 1};
    
  fetchInfo() async {
      return Future.delayed(const Duraction(seconds: 2));
   }
}


main() async {

  final user = User('Majid');
  
  await user.fetchInfo();
  
  print('Hello ${user.id}');
}

https://dartpad.dev/

mhadaily

import 'dart:async';

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    yield i;
  }
}

main() async {
  
  final count = await countStream(10).first;
  print('first count $count');
  
  countStream(10).listen((v) {
    print('Listen to count $v');
  });
}
import { fromEvent } from 'rxjs';
import { throttleTime, scan } from 'rxjs/operators';

fromEvent(document, 'click')
  .pipe(
    throttleTime(1000),
    scan(count => count + 1, 0)
  )
  .subscribe(count => console.log(`Clicked ${count} times`));

https://dartpad.dev/

mhadaily

Everything is a widget

mhadaily

mhadaily

class HelloWorld extends StatelessWidget {
    @override
    Widget build(BuildContext context){
	    return Text('Hello World');
    }
}
class HelloWorld extends HTMLElement {
    connectedCallback(){
    	this.innerHTML = '<p>Hello World</p>';
    }
}

mhadaily

class HelloWorld extends StatelessWidget {
    @override
    Widget build(BuildContext context){
	    return Text('Hello World');
    }
}
class HelloWorld extends React.Component {
    render() {
		return '<p>Hello World</p>';
    }
}

mhadaily

mhadaily

MaterialApp(
    home: Scaffold(
    	appBar: AppBar(
          title: Text('Hello World!'),
        ),
        body: Center(
          child: Text(
            'Majid Hajian',
             style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
    ),
);

mhadaily

<my-app-header>
    <hello-world bold></hello-world>
    
</my-app-header>

<div style="font-weight:bold; text-align:center;">
	Majid Hajian
</div>

mhadaily

No CSS

MaterialApp(
    home: Scaffold(
    	appBar: AppBar(
          title: Text('Hello World!'),
        ),
        body: Center(
          child: Padding(
            padding: EdgeInsets.all(10.0),
            child: const Text(
            'Majid Hajian',
             style: TextStyle(
             	fontWeight: FontWeight.bold
             ),
           ),
          ),
        ),
    ),
);
<style>
  .myclass{
  font-weight:bold; 
  text-align:center;
  padding: 10rem;
  }
</style>

<div class="myclass">
	Majid Hajian
</div>

mhadaily

No CSS

var container = Container( 
  child: Center(
    child: Container(
      child: Text(
        "Lorem ipsum",
        style: bold24Roboto,
      ),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: const Alignment(0.0, -1.0),
          end: const Alignment(0.0, 0.6),
          colors: <Color>[
            const Color(0xffef5350),
            const Color(0x00ef5350)
          ],
        ),
      ), 
      padding: EdgeInsets.all(16),
    ),
  ),
  width: 320,
  height: 240,
  color: Colors.grey[300],
);
<style>
  .myclass{
      background: linear-gradient(180deg, #ef5350, rgba(0, 0, 0, 0) 80%); 
  }
</style>

<div class="myclass">
	Majid Hajian
</div>

mhadaily

No Template No Markup

MaterialApp(
    home: Scaffold(
    	appBar: AppBar(
          title: Text('Hello World!'),
        ),
        body: Center(
          child: Text(
            'Majid Hajian',
             style: TextStyle(
             	fontWeight: FontWeight.bold
             ),
          ),
        ),
    ),
);

mhadaily

Event

  Widget build(BuildContext context) {
    return GestureDetector(
        child: AbsorbPointer(child : child),
        onTap: () {
          print('tap');
          onTap();
        },
        onTapCancel: () {
          print('tapCancel');
        }
      );
  }

mhadaily

Inherited

Widget

StoreProvider<int>{
    store: store,
    child: Text('Flutter Web!'),
}

mhadaily

mhadaily





MaterialApp(
  initialRoute: "/login",
  routes: {
    "/login": (context) => LoginPage(),
    "/home": (context) => HomePage()
  }
);

mhadaily

Use the LayoutBuilder class

Use the MediaQuery.of() method in your build functions

mhadaily

Responsive

Design

class ResponsiveWidget {
  ...
  //Large screen is any screen whose width is more than 1200 pixels
  static bool isLargeScreen(BuildContext context) {
    return MediaQuery.of(context).size.width > 1200;
  }
//Small screen is any screen whose width is less than 800 pixels
  static bool isSmallScreen(BuildContext context) {
    return MediaQuery.of(context).size.width < 800;
  }
//Medium screen is any screen whose width is less than 1200 pixels,
  //and more than 800 pixels
  static bool isMediumScreen(BuildContext context) {
    return MediaQuery.of(context).size.width > 800 &&
    MediaQuery.of(context).size.width < 1200;
  }  
}





import 'package:landingpage/utils/responsive_widget.dart';
class Body extends StatelessWidget {
@override
  Widget build(BuildContext context) {
    return ResponsiveWidget(
      largeScreen: LargeScreen(),
      smallScreen: SmallScreen(),
    );
  }
  ...
}

mhadaily

Animation

class AnimatedCount extends ImplicitlyAnimatedWidget {
  final int count;

  AnimatedCount({
    Key key,
    @required this.count,
    @required Duration duration,
    Curve curve = Curves.linear
  }) : super(duration: duration, curve: curve, key: key);

  @override
  ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState() => _AnimatedCountState();
}

class _AnimatedCountState extends AnimatedWidgetBaseState<AnimatedCount> {
  IntTween _count;

  @override
  Widget build(BuildContext context) {
    return Text(_count.evaluate(animation).toString());
  }

  @override
  void forEachTween(TweenVisitor visitor) {
    _count = visitor(_count, widget.count, (dynamic value) => IntTween(begin: value));
  }
}

mhadaily

PWA

Progressive Web App

<!DOCTYPE html>
<html>
<head>
  <base href="/">
  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">
  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="gooey_edge">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">
  <!-- Favicon -->
  <link rel="icon" type="image/png" href="favicon.png"/>
  <title>hello</title>
  <link rel="manifest" href="manifest.json">
</head>
<body>
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('flutter-first-frame', function () {
        navigator.serviceWorker.register('flutter_service_worker.js');
      });
    }
  </script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

mhadaily

Debugging

inspection

mhadaily

Modern

WEB

Web Assembly 

WebGL

Layout and Paint API (Worklets)

Service workers

PWAs

and more ...

mhadaily

Community

Ecosystem

  • Amazing community
  • Lots of plugins / packages
  • pub.dev

How to Start!

mhadaily

mhadaily

 // Install Flutter SDK 
 // flutter.dev/docs/get-started/install

 flutter channel stable
 flutter upgrade
 flutter create myapp
 cd myapp
 flutter run -d chrome
├── README.md
├── android
├── build
│   └── web
├── ios
├── lib
│   └── main.dart
├── pubspec.lock
├── pubspec.yaml
├── pwa_flutter.iml
├── test
│   └── widget_test.dart
└── web
    ├── favicon.png
    ├── icons
    ├── index.html
    └── manifest.json

mhadaily

After production build

static assets

Write you code

Html Entry file

Similar to package.json

Demo

Credit: https://github.com/MarcusNg/flutter_facebook_responsive_ui

mhadaily

flutter.gskinner.com

Summary

  • Dart vs JS / TS
  • Flutter vs Web / Web vs Flutter?
  • How Flutter web works!

Flutter

Opens a new window for many developers to support several platforms with one single code base without compromising performance.

But one more thing...

Flutter web still needs improvement!

  • Better accessibility 
  • UI interaction improvement towards web convention
  • Performance improvment but overally it's good
  • Better SEO

Read more: https://medium.com/flutter/flutter-web-support-hits-the-stable-milestone-d6b84e83b425

  • Codesize

Learn more

  • flutter.dev/docs/get-started/flutter-for/web-devs
  • flutter.dev/docs/get-started/web
  • events.withgoogle.com/30-daysofflutter/
  • flutter.dev/docs/get-started/flutter-for/declarative

Majid Hajian

mhadaily

Slides and link to source code

bit.ly/web-flutter

SVG icons credited to undraw.co