Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Sunny_tech_flutter

 Sunny_tech_flutter

Après Facebook et react-native, le géant google arrive enfin sur le marché des technos cross-plateforme avec son framework Flutter.

Venez découvrir comment créer très rapidement et facilement une application compatible avec Android et iOS avec un seul code source.

Ce talk sera porté sur la UI (représentation de l'écran en code Flutter) et ne comprendra pas de live coding mais une introduction et d'une explication pas à pas d'un exemple d'application, et sera fait de sorte qu'il soit compréhensible même pour des non-dev android

Florent CHAMPIGNY

June 28, 2019
Tweet

More Decks by Florent CHAMPIGNY

Other Decks in Programming

Transcript

  1. Découvrez Flutter https://flutter.dev/ • Design beautiful apps • Productively build

    apps • Create faster apps (60fps) • Publish mobile, web, & desktop apps
  2. Declarative Tree no more View (xml, xib) / Controller Reactive

    programming Dart 2.0 interpreted on debug, compiled on release Flutter Hot reload
  3. class TweetLabels extends StatelessWidget { @override Widget build(BuildContext context) {

    return Row( children: [ Text( "La plume du figaro" ), SizedBox(width: 6), //spacing Text( "@FigaroPlume" ), SizedBox(width: 6), //spacing Text( "27 mins" ), ], ); } }
  4. Text( "La plume du figaro", style: TextStyle( color: Colors.black, fontWeight:

    FontWeight.w700, fontSize: 16, ), ), SizedBox(width: 6), //spacing Text( "@FigaroPlume", style: TextStyle( color: Colors.grey, fontWeight: FontWeight.w400, fontSize: 14, ), ), SizedBox(width: 6), //spacing Text( "27 mins", style: TextStyle( color: Colors.grey, fontWeight: FontWeight.w600, fontSize: 14, ), ),
  5. class TweetLabels extends StatelessWidget { String accountName; String accountPseudo; String

    tweetTime; TweetLabels({Key key, this.accountName, this.accountPseudo, this.tweetTime}) : super(key: key); @override Widget build(BuildContext context) { return Row( children: ... ); } } StatelessWidgets : updated when re-created StatefullWidget : can be updated with a setState method
  6. Row( children: <Widget>[ Text( this.accountName, //"La plume du figaro", maxLines:

    1, style: … ), SizedBox(width: 6), Flexible( child: Text( this.accountPseudo, //"@FigaroPlume", maxLines: 1, overflow: TextOverflow.ellipsis, style: … ), ), SizedBox(width: 6), Text( this.tweetTime, //"27 mins", style: … ), ], );
  7. class TweetButtons extends StatelessWidget { @override Widget build(BuildContext context) {

    return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-reply-outline.png"), SizedBox(width: 4), //margin Text("1") ], ); } }
  8. return Row( children: [ Expanded( flex: 1, child: Row( mainAxisAlignment:

    MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-reply-outline.png"), SizedBox(width: 4), Text("1") ])), Expanded( flex: 1, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-retweet.png"), SizedBox(width: 4), Text("1") ])), Expanded( flex: 1, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-like-outline.png"), SizedBox(width: 4), Text("1") ])), Expanded( flex: 1, child: Image.asset("assets/twitter-icons/twitter-share.png"), ) ]); Expanded( flex: 1, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-reply-outline.png"), SizedBox(width: 4), Text("1") ])) Expanded( flex: 1, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-reply-outline.png"), SizedBox(width: 4), Text("1") ]))
  9. Expanded( flex: 1, child: GestureDetector( onTap: () { //clicked },

    child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-reply-outline.png"), SizedBox(width: 4), Text("1") ]), ))
  10. class TweetButtons extends StatelessWidget { TweetButtons({Key key}) : super(key: key);

    @override Widget build(BuildContext context) { return Row( children: <Widget>[ Expanded( flex: 1, child: GestureDetector( onTap: () { /* click happened */ }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-reply-outline.png"), SizedBox(width: 4), Text("1") ], ),
  11. class TweetButtons extends StatelessWidget { Function commentClicked; TweetButtons({Key key, this.commentClicked})

    : super(key: key); @override Widget build(BuildContext context) { return Row( children: <Widget>[ Expanded( flex: 1, child: GestureDetector( onTap: commentClicked, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("assets/twitter-icons/twitter-reply-outline.png"), SizedBox(width: 4), Text("1") ], ),
  12. void main() => runApp(MyApp()); class MyApp extends StatelessWidget { Tweet

    tweet1 = Tweet(…); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Twitter', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: TweetView(tweet: tweet1) ), ); } }
  13. void main() => runApp(MyApp()); class MyApp extends StatelessWidget { Tweet

    tweet1 = Tweet(…); Tweet tweet2 = Tweet(…); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Twitter', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: ListView(children: [ TweetView(tweet: tweet1), TweetView(tweet: tweet2), ] ), ); } }
  14. void main() => runApp(MyApp()); class MyApp extends StatelessWidget { List<Tweet>

    tweets = […]; @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Twitter', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: ListView(children: tweets .map( (t) => TweetView(tweet: t) ) .toList() ), ); } }
  15. dependencies: http: ^0.12.0+1 json_annotation: ^2.0.0 dev_dependencies: json_serializable: ^2.0.0 build_runner: ^1.0.0

    pubspecs.yaml https://pub.dev/packages/ Flutter Télécharger et parser du json
  16. GET https://api.tweeter.com/myTweets Télécharger et parser du json [ { "tweet":

    "Ces mots qu'on utilise mal sans le savoir", "id": 1001, "author" : { "imageUrl": "http://lorempixel.com/400/400/", "name": "La plume du figaro", "pseudo" "@FigaroPlume" }, "link" : { "imageUrl": "http://lorempixel.com/800/800/", "description": "Ces mots qu'on utilise mal sans le savoir", "website": "lefigaro.fr" }, "comments": 1, "retweets": 0, "likes": 0 } ]
  17. class Tweet { int id; String text; Author author; Link

    link; int comments; int retweets; int likes; } class Author { String imageUrl; String name; String pseudo; } class Link { String imageUrl; String description; String website; } Télécharger et parser du json
  18. class Tweet { int id; String text; Author author; Link

    link; int comments; int retweets; int likes; } Télécharger et parser du json
  19. class Tweet { int id; String text; Author author; Link

    link; int comments; int retweets; int likes; Tweet({this.id, this.text, this.author, this.link, this.comments, this.retweets, this.likes}); } Télécharger et parser du json
  20. import 'package:json_annotation/json_annotation.dart'; @JsonSerializable() class Tweet { int id; String text;

    Author author; Link link; int comments; int retweets; int likes; Tweet({this.id, this.text, this.author, this.link, this.comments, this.retweets, this.likes}); }
  21. import 'package:json_annotation/json_annotation.dart'; part 'Tweet.g.dart'; @JsonSerializable() class Tweet { int id;

    @JsonKey(name: "tweet") String text; Author author; Link link; int comments; int retweets; int likes; Tweet({this.id, this.text, this.author, this.link, this.comments, this.retweets, this.likes}); factory Tweet.fromJson(Map<String, dynamic> json) => _$TweetFromJson(json); Map<String, dynamic> toJson() => _$TweetToJson(this); }
  22. GET https://api.tweeter.com/myTweets Télécharger et parser du json [ { "tweet":

    "Ces mots qu'on utilise mal sans le savoir", "id": 1001, "author" : { "imageUrl": "http://lorempixel.com/400/400/", "name": "La plume du figaro", "pseudo" "@FigaroPlume" }, "link" : { "imageUrl": "http://lorempixel.com/800/800/", "description": "Ces mots qu'on utilise mal sans le savoir", "website": "lefigaro.fr" }, "comments": 1, "retweets": 0, "likes": 0 } ]
  23. import 'dart:convert'; import 'package:http/http.dart' as http; import 'dart:async'; import 'models/Tweet.dart';

    class Webservice { Future<List<Tweet>> myTweets() async { final response = await http.get( "https://api.tweeter.com/myTweets" ); if (response.statusCode == 200) { var jsonList = json.decode(response.body); return jsonList.map( (model) => Tweet.fromJson(model) ).toList(); } return null; } }
  24. class MyApp extends StatelessWidget { List<Tweet> tweets = [...]; @override

    Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Twitter', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: ListView(children: tweets.map( (t) => TweetView(tweet: t) ).toList() ); ), ); } }
  25. class MyApp extends StatelessWidget { Webservice webservice = Webservice(); @override

    Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Twitter', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: FutureBuilder( future: webservice.myTweets(), builder: (context, snapshot) { if (snapshot.hasData) { List<Tweet> tweets = snapshot.data; return ListView(children: tweets.map( (t) => TweetView(tweet: t) ).toList() ); } else { return ProgressIndicator(); } }),
  26. A flutter project contains 3 directories - lib : for

    dart files - android : an android project - ios : an xcode project (with CocoaPods) Use Platform Channels to communicate between platforms Flutter Platform Channels can be opened from each platform - android -> Flutter - Flutter -> android - Flutter -> android
  27. Flutter -Flutter -> android import 'package:flutter/services.dart'; class ShareManager { MethodChannel

    _channel = MethodChannel('flutter.twitter/share'); void share(String link) async { try { await _channel.invokeMethod('share', link); } catch (e) { print(e); } } } On Flutter
  28. Flutter -Flutter -> android TweetButtons( shareClicked: () { ShareManager().share("http://twitter.com/${tweet.id}"); },

    retweetClicked: () {}, likeClicked: () {}, commentClicked: () {}, ) On Flutter
  29. Flutter -Flutter -> android class MainActivity : FlutterActivity() { override

    fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) GeneratedPluginRegistrant.registerWith(this) //insert your code here } } /android/…/MainActivity.kt On android
  30. Flutter -Flutter -> android //insert your code here val shareChannel

    = MethodChannel(flutterView, "flutter.twitter/share") shareChannel.setMethodCallHandler { methodCall, result -> when (methodCall.method) { "share" -> { val url = methodCall.arguments as String startActivity( Intent.createChooser(Intent().apply { action = Intent.ACTION_SEND type = "text/plain" putExtra(Intent.EXTRA_TEXT, "Please open $url") }, "Share")) result.success(null) } } } On android