SlideShare a Scribd company logo
1 of 51
Download to read offline
Flutter
Learn to build native mobile apps
with Flutter.
Dave Chao
Basic
BLoC
Restful
GraphQL
Agenda
Basic
Flutter System Overview
Graphics Engine
“Everything’s a Widget.
Describes the
configuration for an
Element.
An instantiation of a
Widget at a particular
location in the tree.
Handles size, layout,
and painting.
Flutter has three trees.
Element - Lifecycle RenderObject - PaintWidget - Configure
- https://www.youtube.com/watch?v=996ZgFRENMs
Lifecycle - Stateless Widget
Don’t rebuild, and it is immutable, you can’t change the state of the widget.
1. createElement
2. build
Lifecycle - Stateful Widget
Rebuild, It is mutable, and the state of the Widget can be changed and
it will be notified when a state changed.
1. createState
● When creating StatefulWidget, it will be called.
2. initState
● Only be called once.
3. didChangeDependencies
● After initState, it will be called.
● When the InheritedWidget changes, it will be called.
4. build
Lifecycle - Stateful Widget
5. didUpdateWidget
● After build, it will be called.
● After hot reload, it will be called.
● If the parent widget calls setState(), the child widget's didUpdateWidget will be called.
6. setState
● Call setState, build and redraw.
7. build
8. deactivate
● It will only be called when the State Widget is temporarily removed,
such as the page is changed.
9. dispose
● The State Widget will only be called when it is destroyed forever.
Interact with Widget
Remove widget or leave page
What seems to be the problem?
- https://flutter.dev/docs/development/data-and-backend/state-mgmt/options
BLoC
“BLoC make it easy to separate presentation from
business login, making your code fast, easy to test,
and reusable.
- https://bloclibrary.dev/#/
BLoC Widgets
It is used as a DI widget so that a
single instance of a bloc can be
provided to multiple widgets within
a subtree.
BlocProvider
BLoC Widgets
Handles building the widget in
response to new states.
BlocBuilder
BLoC Widgets
Invokes the listener in response to
state changes in the bloc, such as
navigation, showing a SnackBar,
showing a Dialog.
BlocListener
Getting Started
- Animation
Events
enum AnimationEvent {
idle,
cover_eyes_in,
cover_eyes_out,
success,
fail
}
BLoC
class AnimationBloc extends Bloc<AnimationEvent, String> {
@override
String get initialState => "idle";
@override
Stream<String> mapEventToState(AnimationEvent event) async* {
switch (event) {
case AnimationEvent.idle:
yield "idle";
break;
case AnimationEvent.cover_eyes_in:
yield "cover_eyes_in";
break;
case AnimationEvent.cover_eyes_out:
yield "cover_eyes_out";
break;
case AnimationEvent.success:
yield "success";
break;
case AnimationEvent.fail:
yield "fail";
break;
}}
}
在mapEventToState中,
定義Business Login
Event與State的資料型態
UI
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: BlocProvider<AnimationBloc>(
create: (context) => AnimationBloc(),
child: LoginPage(),
),
);
}
}
建立AnimationBloc, 再透過
BlocProvider注入到LoginPage中
UI
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
AnimationBloc animationBloc;
@override
initState() {
super.initState();
animationBloc = BlocProvider.of<AnimationBloc>(context);
_passwordFocusNode.addListener(() {
if (_passwordFocusNode.hasFocus) {
animationBloc.add(AnimationEvent.cover_eyes_in);
}
});
}
}
● 在LoginPage中, 透過BlocProvider.of
取得AnimationBloc
● 透過Bloc.add(Event Type), 告知Bloc,
依據不同的Event做不同的business
logic
UI
Widget _buildGuss() {
return BlocBuilder<AnimationBloc, String>(builder: (context, state) {
return Container(
height: 200,
padding: EdgeInsets.only(left: 30.0, right: 30.0),
child: FlareActor(
"assets/Guss.flr",
shouldClip: false,
alignment: Alignment.topCenter,
fit: BoxFit.cover,
animation: state,
),
);
});
}
透過BlocBuilder監聽Bloc的state,
依據不同的state, 繪製不同的view
JSON Serializable
JSON Serializable
part 'login_item.g.dart';
@JsonSerializable(nullable: false)
class LoginItem {
@JsonKey(name: "accessToken") String accessToken;
@JsonKey(name: "expiresIn") int expiresIn;
@JsonKey(name: "refreshExpiresIn") int refreshExpiresIn;
@JsonKey(name: "refreshToken") String refreshToken;
LoginItem(this.accessToken, this.expiresIn, this.refreshExpiresIn, this.refreshToken);
factory LoginItem.fromJson(Map<String, dynamic> json) =>
_$LoginItemFromJson(json);
Map<String, dynamic> toJson() => _$LoginItemToJson(this);
}
Generate Code:
flutter packages pub run build_runner build
JSON Serializable
part of 'login_item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
LoginItem _$LoginItemFromJson(Map<String, dynamic> json) {
return LoginItem(
json['accessToken'] as String,
json['expiresIn'] as int,
json['refreshExpiresIn'] as int,
json['refreshToken'] as String,
);
}
Map<String, dynamic> _$LoginItemToJson(LoginItem instance) => <String, dynamic>{
'accessToken': instance.accessToken,
'expiresIn': instance.expiresIn,
'refreshExpiresIn': instance.refreshExpiresIn,
'refreshToken': instance.refreshToken,
};
Restful
“Dio is a powerful Http client, which supports
Interceptors, Global configuration, FormData,
Request Cancellation, File downloading, Timeout.
- https://pub.dev/packages/dio
Dio
Init
Dio dio = Dio(new BaseOptions(
baseUrl: ‘https://api.xyzabc.com/’,
connectTimeout: 5000,
receiveTimeout: 100000,
contentType: ContentType.json.toString(),
responseType: ResponseType.json,
));
Dio
GET
Future<BikeListItem> fetchBikes() async {
final response = await _dio.get("youbike");
return BikeListItem.fromJson(response.data);
}
POST
Future<LoginItem> login(LoginRequest request) async {
final response = await _dio.post(
"v1/basicLogin",
data: request,
);
return LoginItem.fromJson(response.data["data"]);
}
Interceptors
dio.interceptors.add(InterceptorsWrapper(
onRequest: (RequestOptions options) async {
Fimber.d("headers: ${options.headers}");
Fimber.d("uri: ${options.uri}");
Fimber.d("data: ${options.data}");
Fimber.d("query: ${options.queryParameters}");
return options;
},
onResponse: (Response response) async {
return response;
},
onError: (DioError e) async {
return e;
}
));
Dio
Getting Started
- Login
Events
abstract class LoginEvent extends Equatable {
const LoginEvent();
}
class Login extends LoginEvent {
final LoginRequest request;
Login(this.request);
@override
List<Object> get props => [request];
}
States
class LoginState extends Equatable {
const LoginState();
@override
List<Object> get props => [];
}
class Loading extends LoginState {}
class DataEmpty extends LoginState {}
class Error extends LoginState {}
class Success extends LoginState {
final LoginItem loginItem;
Success([this.loginItem]) : assert(loginItem != null);
@override
List<Object> get props => [loginItem];
}
BLoC
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final AccountRepository repository;
LoginBloc({@required this.repository});
@override
LoginState get initialState => Loading();
@override
Stream<LoginState> mapEventToState(LoginEvent event) async* {
if (event is Login) {
try {
yield Loading();
final loginItem = await repository.login(event.request);
yield Success(loginItem);
} catch (error) {
Fimber.e("Error: $error");
yield Error();
}
}
}
}
Repository
class AccountRepository {
Dio _dio;
AccountRepository(Dio dio) {
_dio = dio;
}
Future<LoginItem> login(LoginRequest request) async {
final response = await _dio.post(
"v1/login",
data: request,
);
Fimber.d("response.statusCode: ${response.statusCode}");
Fimber.d("response.data: ${response.data}");
return LoginItem.fromJson(response.data["data"]);
}
}
UI
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
Config _config = ConfigProvider.of(context).config;
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiBlocProvider(
providers: [
BlocProvider<LoginBloc>(create: (context) => LoginBloc(repository: AccountRepository(_config.dio))),
BlocProvider<AnimationBloc>(create: (context) => AnimationBloc()),
],
child: LoginPage(),
),
);
}
}
UI
LoginBloc loginBloc = BlocProvider.of<LoginBloc>(context);
onTap: () {
if (_formKey.currentState.validate()) {
final account = _accountController.text.toString();
final password = _passwordController.text.toString();
final request = LoginRequest("app.internal", account, password);
loginBloc.add(Login(request));
}
}
BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
if (state is Success) {
_navigateToHomeScreen();
} else if (state is Error) {
animationBloc.add(AnimationEvent.fail);
}
}
GraphQL
Init GraphQL Client
GraphQLClient _initGraphQLClient() {
final HttpLink _httpLink = HttpLink(
uri: 'http://11.222.333.444:8888/graphql',
headers: {
Constant.X_GRAPH_AUTH: Constant.X_GRAPH_AUTH_VALUE,
},
);
return GraphQLClient(
cache: InMemoryCache(),
link: _httpLink,
);
}
Query
const String readPodcasts = r'''
query ReadPodcasts {
podcast {
id,
artistName,
name,
artworkUrl100
}
}
''';
Query
Future<QueryResult> getPodCasts() async {
final QueryOptions _options = QueryOptions(
documentNode: gql(queries.readPodCasts),
);
return await client.query(_options);
}
Mutation
const String addUser = r'''
mutation AddUser($name: String!) {
id,
name
}
''';
Mutation
Future<QueryResult> addUser(String name) async {
final MutationOptions _options = MutationOptions(
documentNode: gql(mutations.adduser),
variables: <String, dynamic>{name: name},
);
return await client.mutate(_options);
}
Getting Started
- Fetch Collections
Query Language
const String readPodCastDetail = r'''
query ReadPodCastDetail($collectionId: String!) {
collection(id: $collectionId) {
artistId,
artistName,
artworkUrl100,
artworkUrl600,
collectionId,
collectionName,
country,
genreIds,
genres,
releaseDate,
contentFeed {
contentUrl,
desc,
publishedDate,
title
}
}
}
''';
Events
abstract class CollectionEvent extends Equatable {
const CollectionEvent();
}
class FetchPodCastDetail extends CollectionEvent {
final String collectionId;
FetchPodCastDetail(this.collectionId);
@override
List<Object> get props => [collectionId];
}
States
class CollectionState extends Equatable {
const CollectionState();
@override
List<Object> get props => [];
}
class Loading extends CollectionState {}
class DataEmpty extends CollectionState {}
class Error extends CollectionState {}
class Success extends CollectionState {
final PodCastDetailItem podCastDetailItem;
Success([this.podCastDetailItem]) : assert(podCastDetailItem != null);
@override
List<Object> get props => [podCastDetailItem];
}
BLoC
class CollectionBloc extends Bloc<CollectionEvent, CollectionState> {
final PodCastRepository repository;
CollectionBloc({@required this.repository,});
@override
CollectionState get initialState => Loading();
@override
Stream<CollectionState> mapEventToState(CollectionEvent event) async* {
if (event is FetchPodCastDetail) {
try {
final result = await repository.getPodCastDetail(event.collectionId);
final podCastDetailItem = PodCastDetailItem.fromJson(result.data);
yield Success(podCastDetailItem);
} catch (error) {
Fimber.e("Error: $error");
yield Error(error);
}
}
}
}
Repository
class PodCastRepository {
final GraphQLClient client;
PodCastRepository({@required this.client}) : assert(client != null);
Future<QueryResult> getPodCasts() async {
final QueryOptions _options = QueryOptions(documentNode: gql(queries.readPodCasts),);
return await client.query(_options);
}
Future<QueryResult> getPodCastDetail(String collectionId) async {
final QueryOptions _options = QueryOptions(
documentNode: gql(queries.readPodCastDetail),
variables: <String, dynamic>{'collectionId': collectionId},
);
return await client.query(_options);
}
}
UI
CollectionBloc bloc = BlocProvider.of<CollectionBloc>(context);
bloc.add(FetchPodCastDetail(widget.collectionId));
BlocBuilder<CollectionBloc, CollectionState>(
builder: (context, state) {
if (state is Success) {
final collectionItem = state.podCastDetailItem.collectionItem;
return Column(
children: <Widget>[
_buildCastDetail(collectionItem),
SizedBox(height: 16.0),
_buildTitle(),
_buildContentFeed(collectionItem)
],
);
} else {
return CustomerProgressIndicator();
}
}
Podcasts App
- https://github.com/davechao/podcast_app
Thanks!
Does anyone have any questions?

More Related Content

What's hot

What is flutter and why should i care?
What is flutter and why should i care?What is flutter and why should i care?
What is flutter and why should i care?Sergi Martínez
 
What and Why Flutter? What is a Widget in Flutter?
What and Why Flutter? What is a Widget in Flutter?What and Why Flutter? What is a Widget in Flutter?
What and Why Flutter? What is a Widget in Flutter?MohammadHussain595488
 
Getting started with flutter
Getting started with flutterGetting started with flutter
Getting started with flutterrihannakedy
 
Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101Arif Amirani
 
INTRODUCTION TO FLUTTER BASICS.pptx
INTRODUCTION TO FLUTTER BASICS.pptxINTRODUCTION TO FLUTTER BASICS.pptx
INTRODUCTION TO FLUTTER BASICS.pptx20TUCS033DHAMODHARAK
 
The magic of flutter
The magic of flutterThe magic of flutter
The magic of flutterShady Selim
 
Build beautiful native apps in record time with flutter
Build beautiful native apps in record time with flutterBuild beautiful native apps in record time with flutter
Build beautiful native apps in record time with flutterRobertLe30
 
Flutter talkshow
Flutter talkshowFlutter talkshow
Flutter talkshowNhan Cao
 
Flutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | EdurekaFlutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | EdurekaEdureka!
 
Developing Cross platform apps in flutter (Android, iOS, Web)
Developing Cross platform apps in flutter (Android, iOS, Web)Developing Cross platform apps in flutter (Android, iOS, Web)
Developing Cross platform apps in flutter (Android, iOS, Web)Priyanka Tyagi
 
Intro to Flutter SDK
Intro to Flutter SDKIntro to Flutter SDK
Intro to Flutter SDKdigitaljoni
 
DSC IIITL Flutter Workshop
DSC IIITL Flutter WorkshopDSC IIITL Flutter Workshop
DSC IIITL Flutter WorkshopDSCIIITLucknow
 
Flutter for web
Flutter for web Flutter for web
Flutter for web rihannakedy
 
Flutter state management from zero to hero
Flutter state management from zero to heroFlutter state management from zero to hero
Flutter state management from zero to heroAhmed Abu Eldahab
 

What's hot (20)

What is flutter and why should i care?
What is flutter and why should i care?What is flutter and why should i care?
What is flutter and why should i care?
 
What and Why Flutter? What is a Widget in Flutter?
What and Why Flutter? What is a Widget in Flutter?What and Why Flutter? What is a Widget in Flutter?
What and Why Flutter? What is a Widget in Flutter?
 
Getting started with flutter
Getting started with flutterGetting started with flutter
Getting started with flutter
 
Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101
 
INTRODUCTION TO FLUTTER BASICS.pptx
INTRODUCTION TO FLUTTER BASICS.pptxINTRODUCTION TO FLUTTER BASICS.pptx
INTRODUCTION TO FLUTTER BASICS.pptx
 
The magic of flutter
The magic of flutterThe magic of flutter
The magic of flutter
 
Flutter
FlutterFlutter
Flutter
 
Flutter for web
Flutter for webFlutter for web
Flutter for web
 
Build beautiful native apps in record time with flutter
Build beautiful native apps in record time with flutterBuild beautiful native apps in record time with flutter
Build beautiful native apps in record time with flutter
 
Flutter workshop
Flutter workshopFlutter workshop
Flutter workshop
 
Flutter talkshow
Flutter talkshowFlutter talkshow
Flutter talkshow
 
Flutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | EdurekaFlutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | Edureka
 
Developing Cross platform apps in flutter (Android, iOS, Web)
Developing Cross platform apps in flutter (Android, iOS, Web)Developing Cross platform apps in flutter (Android, iOS, Web)
Developing Cross platform apps in flutter (Android, iOS, Web)
 
Intro to Flutter SDK
Intro to Flutter SDKIntro to Flutter SDK
Intro to Flutter SDK
 
DSC IIITL Flutter Workshop
DSC IIITL Flutter WorkshopDSC IIITL Flutter Workshop
DSC IIITL Flutter Workshop
 
Flutter
Flutter Flutter
Flutter
 
Flutter for web
Flutter for web Flutter for web
Flutter for web
 
What is Flutter
What is FlutterWhat is Flutter
What is Flutter
 
Flutter
FlutterFlutter
Flutter
 
Flutter state management from zero to hero
Flutter state management from zero to heroFlutter state management from zero to hero
Flutter state management from zero to hero
 

Similar to Flutter

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose worldFabio Collini
 
Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Mahmoud Hamed Mahmoud
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM patternNAVER Engineering
 
Android Architecture Components
Android Architecture ComponentsAndroid Architecture Components
Android Architecture ComponentsBurhanuddinRashid
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesRiad Benguella
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developersPavel Lahoda
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon Berlin
 
React 16: new features and beyond
React 16: new features and beyondReact 16: new features and beyond
React 16: new features and beyondArtjoker
 
#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps runFrederik De Bruyne
 
MVI - Managing State The Kotlin Way
MVI - Managing State The Kotlin WayMVI - Managing State The Kotlin Way
MVI - Managing State The Kotlin WayZeyad Gasser
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCpootsbook
 
BLoC - Be Reactive in flutter
BLoC - Be Reactive in flutterBLoC - Be Reactive in flutter
BLoC - Be Reactive in flutterGiacomo Ranieri
 
Declarative presentations UIKonf
Declarative presentations UIKonfDeclarative presentations UIKonf
Declarative presentations UIKonfNataliya Patsovska
 
[FEConf Korea 2017]Angular 컴포넌트 대화법
[FEConf Korea 2017]Angular 컴포넌트 대화법[FEConf Korea 2017]Angular 컴포넌트 대화법
[FEConf Korea 2017]Angular 컴포넌트 대화법Jeado Ko
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners Varun Raj
 

Similar to Flutter (20)

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
Android Architecture Components
Android Architecture ComponentsAndroid Architecture Components
Android Architecture Components
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisables
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
 
Geb qa fest2017
Geb qa fest2017Geb qa fest2017
Geb qa fest2017
 
PLM and Background Tasks by Jason Fox
PLM and Background Tasks by Jason FoxPLM and Background Tasks by Jason Fox
PLM and Background Tasks by Jason Fox
 
React 16: new features and beyond
React 16: new features and beyondReact 16: new features and beyond
React 16: new features and beyond
 
#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run
 
Frontin like-a-backer
Frontin like-a-backerFrontin like-a-backer
Frontin like-a-backer
 
MVI - Managing State The Kotlin Way
MVI - Managing State The Kotlin WayMVI - Managing State The Kotlin Way
MVI - Managing State The Kotlin Way
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
 
BLoC - Be Reactive in flutter
BLoC - Be Reactive in flutterBLoC - Be Reactive in flutter
BLoC - Be Reactive in flutter
 
React/Redux
React/ReduxReact/Redux
React/Redux
 
Declarative presentations UIKonf
Declarative presentations UIKonfDeclarative presentations UIKonf
Declarative presentations UIKonf
 
An intro to cqrs
An intro to cqrsAn intro to cqrs
An intro to cqrs
 
[FEConf Korea 2017]Angular 컴포넌트 대화법
[FEConf Korea 2017]Angular 컴포넌트 대화법[FEConf Korea 2017]Angular 컴포넌트 대화법
[FEConf Korea 2017]Angular 컴포넌트 대화법
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
 

Recently uploaded

Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...masabamasaba
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Hararemasabamasaba
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024VictoriaMetrics
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT  - Elevating Productivity in Today's Agile EnvironmentHarnessing ChatGPT  - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT - Elevating Productivity in Today's Agile EnvironmentVictorSzoltysek
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park masabamasaba
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfkalichargn70th171
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 

Recently uploaded (20)

Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT  - Elevating Productivity in Today's Agile EnvironmentHarnessing ChatGPT  - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 

Flutter

  • 1. Flutter Learn to build native mobile apps with Flutter. Dave Chao
  • 6. Describes the configuration for an Element. An instantiation of a Widget at a particular location in the tree. Handles size, layout, and painting. Flutter has three trees. Element - Lifecycle RenderObject - PaintWidget - Configure - https://www.youtube.com/watch?v=996ZgFRENMs
  • 7. Lifecycle - Stateless Widget Don’t rebuild, and it is immutable, you can’t change the state of the widget. 1. createElement 2. build
  • 8. Lifecycle - Stateful Widget Rebuild, It is mutable, and the state of the Widget can be changed and it will be notified when a state changed. 1. createState ● When creating StatefulWidget, it will be called. 2. initState ● Only be called once. 3. didChangeDependencies ● After initState, it will be called. ● When the InheritedWidget changes, it will be called. 4. build
  • 9. Lifecycle - Stateful Widget 5. didUpdateWidget ● After build, it will be called. ● After hot reload, it will be called. ● If the parent widget calls setState(), the child widget's didUpdateWidget will be called. 6. setState ● Call setState, build and redraw. 7. build 8. deactivate ● It will only be called when the State Widget is temporarily removed, such as the page is changed. 9. dispose ● The State Widget will only be called when it is destroyed forever. Interact with Widget Remove widget or leave page
  • 10. What seems to be the problem? - https://flutter.dev/docs/development/data-and-backend/state-mgmt/options
  • 11. BLoC
  • 12. “BLoC make it easy to separate presentation from business login, making your code fast, easy to test, and reusable. - https://bloclibrary.dev/#/
  • 13. BLoC Widgets It is used as a DI widget so that a single instance of a bloc can be provided to multiple widgets within a subtree. BlocProvider
  • 14. BLoC Widgets Handles building the widget in response to new states. BlocBuilder
  • 15. BLoC Widgets Invokes the listener in response to state changes in the bloc, such as navigation, showing a SnackBar, showing a Dialog. BlocListener
  • 18. BLoC class AnimationBloc extends Bloc<AnimationEvent, String> { @override String get initialState => "idle"; @override Stream<String> mapEventToState(AnimationEvent event) async* { switch (event) { case AnimationEvent.idle: yield "idle"; break; case AnimationEvent.cover_eyes_in: yield "cover_eyes_in"; break; case AnimationEvent.cover_eyes_out: yield "cover_eyes_out"; break; case AnimationEvent.success: yield "success"; break; case AnimationEvent.fail: yield "fail"; break; }} } 在mapEventToState中, 定義Business Login Event與State的資料型態
  • 19. UI class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: BlocProvider<AnimationBloc>( create: (context) => AnimationBloc(), child: LoginPage(), ), ); } } 建立AnimationBloc, 再透過 BlocProvider注入到LoginPage中
  • 20. UI class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { AnimationBloc animationBloc; @override initState() { super.initState(); animationBloc = BlocProvider.of<AnimationBloc>(context); _passwordFocusNode.addListener(() { if (_passwordFocusNode.hasFocus) { animationBloc.add(AnimationEvent.cover_eyes_in); } }); } } ● 在LoginPage中, 透過BlocProvider.of 取得AnimationBloc ● 透過Bloc.add(Event Type), 告知Bloc, 依據不同的Event做不同的business logic
  • 21. UI Widget _buildGuss() { return BlocBuilder<AnimationBloc, String>(builder: (context, state) { return Container( height: 200, padding: EdgeInsets.only(left: 30.0, right: 30.0), child: FlareActor( "assets/Guss.flr", shouldClip: false, alignment: Alignment.topCenter, fit: BoxFit.cover, animation: state, ), ); }); } 透過BlocBuilder監聽Bloc的state, 依據不同的state, 繪製不同的view
  • 23. JSON Serializable part 'login_item.g.dart'; @JsonSerializable(nullable: false) class LoginItem { @JsonKey(name: "accessToken") String accessToken; @JsonKey(name: "expiresIn") int expiresIn; @JsonKey(name: "refreshExpiresIn") int refreshExpiresIn; @JsonKey(name: "refreshToken") String refreshToken; LoginItem(this.accessToken, this.expiresIn, this.refreshExpiresIn, this.refreshToken); factory LoginItem.fromJson(Map<String, dynamic> json) => _$LoginItemFromJson(json); Map<String, dynamic> toJson() => _$LoginItemToJson(this); } Generate Code: flutter packages pub run build_runner build
  • 24. JSON Serializable part of 'login_item.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** LoginItem _$LoginItemFromJson(Map<String, dynamic> json) { return LoginItem( json['accessToken'] as String, json['expiresIn'] as int, json['refreshExpiresIn'] as int, json['refreshToken'] as String, ); } Map<String, dynamic> _$LoginItemToJson(LoginItem instance) => <String, dynamic>{ 'accessToken': instance.accessToken, 'expiresIn': instance.expiresIn, 'refreshExpiresIn': instance.refreshExpiresIn, 'refreshToken': instance.refreshToken, };
  • 26. “Dio is a powerful Http client, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout. - https://pub.dev/packages/dio
  • 27. Dio Init Dio dio = Dio(new BaseOptions( baseUrl: ‘https://api.xyzabc.com/’, connectTimeout: 5000, receiveTimeout: 100000, contentType: ContentType.json.toString(), responseType: ResponseType.json, ));
  • 28. Dio GET Future<BikeListItem> fetchBikes() async { final response = await _dio.get("youbike"); return BikeListItem.fromJson(response.data); } POST Future<LoginItem> login(LoginRequest request) async { final response = await _dio.post( "v1/basicLogin", data: request, ); return LoginItem.fromJson(response.data["data"]); }
  • 29. Interceptors dio.interceptors.add(InterceptorsWrapper( onRequest: (RequestOptions options) async { Fimber.d("headers: ${options.headers}"); Fimber.d("uri: ${options.uri}"); Fimber.d("data: ${options.data}"); Fimber.d("query: ${options.queryParameters}"); return options; }, onResponse: (Response response) async { return response; }, onError: (DioError e) async { return e; } )); Dio
  • 31. Events abstract class LoginEvent extends Equatable { const LoginEvent(); } class Login extends LoginEvent { final LoginRequest request; Login(this.request); @override List<Object> get props => [request]; }
  • 32. States class LoginState extends Equatable { const LoginState(); @override List<Object> get props => []; } class Loading extends LoginState {} class DataEmpty extends LoginState {} class Error extends LoginState {} class Success extends LoginState { final LoginItem loginItem; Success([this.loginItem]) : assert(loginItem != null); @override List<Object> get props => [loginItem]; }
  • 33. BLoC class LoginBloc extends Bloc<LoginEvent, LoginState> { final AccountRepository repository; LoginBloc({@required this.repository}); @override LoginState get initialState => Loading(); @override Stream<LoginState> mapEventToState(LoginEvent event) async* { if (event is Login) { try { yield Loading(); final loginItem = await repository.login(event.request); yield Success(loginItem); } catch (error) { Fimber.e("Error: $error"); yield Error(); } } } }
  • 34. Repository class AccountRepository { Dio _dio; AccountRepository(Dio dio) { _dio = dio; } Future<LoginItem> login(LoginRequest request) async { final response = await _dio.post( "v1/login", data: request, ); Fimber.d("response.statusCode: ${response.statusCode}"); Fimber.d("response.data: ${response.data}"); return LoginItem.fromJson(response.data["data"]); } }
  • 35. UI class App extends StatelessWidget { @override Widget build(BuildContext context) { Config _config = ConfigProvider.of(context).config; return MaterialApp( debugShowCheckedModeBanner: false, home: MultiBlocProvider( providers: [ BlocProvider<LoginBloc>(create: (context) => LoginBloc(repository: AccountRepository(_config.dio))), BlocProvider<AnimationBloc>(create: (context) => AnimationBloc()), ], child: LoginPage(), ), ); } }
  • 36. UI LoginBloc loginBloc = BlocProvider.of<LoginBloc>(context); onTap: () { if (_formKey.currentState.validate()) { final account = _accountController.text.toString(); final password = _passwordController.text.toString(); final request = LoginRequest("app.internal", account, password); loginBloc.add(Login(request)); } } BlocListener<LoginBloc, LoginState>( listener: (context, state) { if (state is Success) { _navigateToHomeScreen(); } else if (state is Error) { animationBloc.add(AnimationEvent.fail); } }
  • 38. Init GraphQL Client GraphQLClient _initGraphQLClient() { final HttpLink _httpLink = HttpLink( uri: 'http://11.222.333.444:8888/graphql', headers: { Constant.X_GRAPH_AUTH: Constant.X_GRAPH_AUTH_VALUE, }, ); return GraphQLClient( cache: InMemoryCache(), link: _httpLink, ); }
  • 39. Query const String readPodcasts = r''' query ReadPodcasts { podcast { id, artistName, name, artworkUrl100 } } ''';
  • 40. Query Future<QueryResult> getPodCasts() async { final QueryOptions _options = QueryOptions( documentNode: gql(queries.readPodCasts), ); return await client.query(_options); }
  • 41. Mutation const String addUser = r''' mutation AddUser($name: String!) { id, name } ''';
  • 42. Mutation Future<QueryResult> addUser(String name) async { final MutationOptions _options = MutationOptions( documentNode: gql(mutations.adduser), variables: <String, dynamic>{name: name}, ); return await client.mutate(_options); }
  • 43. Getting Started - Fetch Collections
  • 44. Query Language const String readPodCastDetail = r''' query ReadPodCastDetail($collectionId: String!) { collection(id: $collectionId) { artistId, artistName, artworkUrl100, artworkUrl600, collectionId, collectionName, country, genreIds, genres, releaseDate, contentFeed { contentUrl, desc, publishedDate, title } } } ''';
  • 45. Events abstract class CollectionEvent extends Equatable { const CollectionEvent(); } class FetchPodCastDetail extends CollectionEvent { final String collectionId; FetchPodCastDetail(this.collectionId); @override List<Object> get props => [collectionId]; }
  • 46. States class CollectionState extends Equatable { const CollectionState(); @override List<Object> get props => []; } class Loading extends CollectionState {} class DataEmpty extends CollectionState {} class Error extends CollectionState {} class Success extends CollectionState { final PodCastDetailItem podCastDetailItem; Success([this.podCastDetailItem]) : assert(podCastDetailItem != null); @override List<Object> get props => [podCastDetailItem]; }
  • 47. BLoC class CollectionBloc extends Bloc<CollectionEvent, CollectionState> { final PodCastRepository repository; CollectionBloc({@required this.repository,}); @override CollectionState get initialState => Loading(); @override Stream<CollectionState> mapEventToState(CollectionEvent event) async* { if (event is FetchPodCastDetail) { try { final result = await repository.getPodCastDetail(event.collectionId); final podCastDetailItem = PodCastDetailItem.fromJson(result.data); yield Success(podCastDetailItem); } catch (error) { Fimber.e("Error: $error"); yield Error(error); } } } }
  • 48. Repository class PodCastRepository { final GraphQLClient client; PodCastRepository({@required this.client}) : assert(client != null); Future<QueryResult> getPodCasts() async { final QueryOptions _options = QueryOptions(documentNode: gql(queries.readPodCasts),); return await client.query(_options); } Future<QueryResult> getPodCastDetail(String collectionId) async { final QueryOptions _options = QueryOptions( documentNode: gql(queries.readPodCastDetail), variables: <String, dynamic>{'collectionId': collectionId}, ); return await client.query(_options); } }
  • 49. UI CollectionBloc bloc = BlocProvider.of<CollectionBloc>(context); bloc.add(FetchPodCastDetail(widget.collectionId)); BlocBuilder<CollectionBloc, CollectionState>( builder: (context, state) { if (state is Success) { final collectionItem = state.podCastDetailItem.collectionItem; return Column( children: <Widget>[ _buildCastDetail(collectionItem), SizedBox(height: 16.0), _buildTitle(), _buildContentFeed(collectionItem) ], ); } else { return CustomerProgressIndicator(); } }
  • 51. Thanks! Does anyone have any questions?