Skip to content

Instantly share code, notes, and snippets.

@andrefauth
Forked from dpossas/README.md
Created September 9, 2020 19:16
Show Gist options
  • Save andrefauth/24b1771d76e7406b2dbea7bfaae45fea to your computer and use it in GitHub Desktop.
Save andrefauth/24b1771d76e7406b2dbea7bfaae45fea to your computer and use it in GitHub Desktop.
Workshop Flutter

Flutter - 3 horas de HandsOn

Informações sobre o Workshop

  1. Construiremos um aplicativo para gerenciamento de coleções HotWheels e abordaremos:

  2. Todo participante deve ter o ambiente flutter configurado com a versão estável (1.20.2 em 19/08/2020) e com o projeto hello world rodando em um emulador Para configurar o ambiente, siga as instruções em: https://flutter.dev/docs/get-started/install

  3. Usaremos o VSCode para desenvolver nosso projeto. Para isso, é necessário instalar o plugin de Flutter nesta IDE Poderão ser usadas outras IDEs, mas talvez eu não consiga lhe ajudar com os atalhos e particularidades :(

É necessário anotar a classe com a anotação

@JsonSerializable()

É necessário usar uma partial como:

 part 'my_class.g.dar';

Método para gerar objetos através de um json

 factory MyClass.fromJson(Map<String, dynamic> json) => _$MyClassFromJson(json);

Método para gerar um json através de um objeto

 Map<String, dynamic> toJson() => _$MyClassToJson(this);

Ao adicionar as linhas anteriores, a IDE acusará um erro. Ignore-o por enquanto

Para rodar o build-runner e gerar as classes necessárias para converter json-object / object-json

 flutter pub run build_runner build

No fim sua classe (my_class.dart) deve ser algo como

import 'package:json_annotation/json_annotation.dart';

part 'my_class.g.dart';

@JsonSerializable()
class MyClass {
  final int id;
  
  MyClass({this.id});
  
  factory MyClass.fromJson(Map<String, dynamic> json) => _$MyClassFromJson(json);
  Map<String, dynamic> toJson() => _$MyClassToJson(this);
}
import 'package:json_annotation/json_annotation.dart';
part "car.g.dart";
@JsonSerializable()
class Car {
@JsonKey(name: 'CarMetaID')
int id;
@JsonKey(
name: 'CarOrderNumber', fromJson: int.tryParse, toJson: objectToString)
int order;
@JsonKey(name: 'TotalItems')
int totalItems;
@JsonKey(name: 'Title')
String name;
@JsonKey(name: 'MiniCollectionId')
int collectionId;
@JsonKey(name: 'ColorCode')
String hexColor;
@JsonKey(name: 'Year')
int year;
@JsonKey(name: 'Packagingcode')
String packageCode;
@JsonKey(name: 'MainImages')
List<dynamic> mainImages;
Car({
this.id,
this.order,
this.name,
this.totalItems,
this.hexColor,
this.year,
this.packageCode,
this.mainImages,
});
factory Car.fromJson(Map<String, dynamic> json) => _$CarFromJson(json);
Map<String, dynamic> toJson() => _$CarToJson(this);
static String objectToString(dynamic v) {
return String.fromCharCode(v);
}
}
{
"CarMetaID": 102186,
"CarOrderNumber": "4",
"TotalItems": 10,
"Title": "'20 JEEP GLADIATOR",
"ToyNumber": "GHB41",
"CarMetaSEOName": "20-jeep-gladiator",
"CategoryId": null,
"MiniCollectionId": 1247,
"MiniCollection": "BAJA BLAZERS",
"GameCategoryId": 0,
"GameCategory": null,
"MiniCollectionUrl": "https://static-nv.mattel.com/HWCarCatalog/en-us/Images/baja-blazers-collection_tcm985-153435.png",
"MakeUrl": null,
"ColorUrl": "https://static.mattel.com/HWCarCatalog/en-us/Images/RED_tcm838-287586.png",
"SeriesUrl": null,
"ColorSEOName": "red",
"ColorCode": "#b2020b",
"MakeSEOName": "jeep",
"MiniCollectionSEOName": "baja-blazers",
"Year": 2020,
"SeriesId": null,
"Series": null,
"MakeId": 1156,
"Make": "Jeep",
"ColorId": 111,
"Color": "Red",
"Packagingcode": "lztsc",
"CarMetaDescription": null,
"LiveDate": null,
"IsNew": 0,
"DisplayFlag": false,
"Createddate": "0001-01-01T00:00:00",
"CreatedBy": null,
"Modifieddate": null,
"ModifiedBy": null,
"Styles": [
{
"Id": 1053,
"Name": "Truck",
"ImageUrl": "https://static-nv.mattel.com/HWCarCatalog/en-us/Images/Style_Truck_330px_tcm985-127487.png",
"Number": 1,
"StyleSEOName": "truck"
}
],
"ThreeQuarterUnity": [],
"Sideviewphotos": [
{
"URL": "https://media.mattel.com/root/HWCarsCatalog/App/SideView/300x164/GHB41_C_003.png",
"Resolution": "300X164",
"ContentId": null
},
{
"URL": "https://media.mattel.com/root/HWCarsCatalog/App/SideView/534x300/GHB41_C_003.png",
"Resolution": "534X300",
"ContentId": null
},
{
"URL": "https://media.mattel.com/root/HWCarsCatalog/App/SideView/710x400/GHB41_C_003.png",
"Resolution": "710X400",
"ContentId": null
}
],
"SideviewGIFphotos": [],
"Topdownphotos": [],
"UnityImages_iOS": [],
"UnityImages_Android": [],
"MainImages": [
{
"URL": "https://media.mattel.com/root/HWCarsCatalog/Web/MainImage/GHB41_C_003.png",
"Resolution": null,
"ContentId": null
}
],
"BrandImages": [],
"ThumbnailImages": [
{
"URL": "https://media.mattel.com/root/HWCarsCatalog/Web/Thumbnail/GHB41_C_003.png",
"Resolution": null,
"ContentId": null
}
],
"carouselImages": [],
"ThreeSixtyDegreeVideo": [],
"AnimationImage": []
}
import 'package:dio/dio.dart';
import 'package:workshop_enterprise/models/car.dart';
import 'package:workshop_enterprise/services/dio_provider_base.dart';
class CarRepository {
static const String URL_GET_CARS = "GetCars";
Dio dio = DioProviderBase().dio;
Future<List<Car>> get() async {
final _cars = List<Car>();
Response response = await dio.post(URL_GET_CARS, data: {});
if (response.statusCode == 200) {
List.castFrom(response.data).forEach(
(element) => _cars.add(Car.fromJson(element)),
);
return _cars;
}
return null;
}
}
{
"Id": 1117,
"Code": "985-120924-1024",
"Name": "HW CITY WORKS",
"BackGroundColor": "#fcde07",
"ForeGroundColor": "#000000",
"SEOName": "hw-city-works",
"TotalItems": 10,
"ImageUrl": "https://static-nv.mattel.com/HWCarCatalog/en-us/Images/HW_Cityworks_330px_tcm985-130360.png"
}
import 'package:flutter/material.dart';
import 'package:workshop_enterprise/models/car.dart';
import 'package:workshop_enterprise/models/hw_collection.dart';
import 'package:workshop_enterprise/repositories/car_repository.dart';
class CollectionDetails extends StatefulWidget {
final MiniCollection miniCollection;
const CollectionDetails({
Key key,
this.miniCollection,
}) : super(key: key);
@override
_CollectionDetailsState createState() => _CollectionDetailsState();
}
class _CollectionDetailsState extends State<CollectionDetails> {
List<int> inCollection = <int>[];
final carRepo = CarRepository();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.miniCollection.name),
centerTitle: true,
),
body: SafeArea(
child: Container(
child: FutureBuilder(
future: carRepo.get(widget.miniCollection.id),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
continue loading;
break;
loading:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
break;
case ConnectionState.active:
// TODO: Handle this case.
break;
case ConnectionState.done:
List<Car> cars = snapshot.data;
return ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
final Car car = cars[index];
return ListTile(
title: Text(car.name),
leading: SizedBox(
width: 40,
height: 40,
child: Image.network(car.mainImages[0]['URL']),
),
subtitle: Text(
"${car.year}",
),
trailing: BookmarkIcon(
inCollection: inCollection,
carId: car.id,
),
);
},
separatorBuilder: (context, index) => Divider(),
itemCount: cars.length,
);
break;
}
return Text('testo');
},
),
),
),
);
}
}
class BookmarkIcon extends StatefulWidget {
List<int> inCollection;
int carId;
BookmarkIcon({Key key, this.inCollection, this.carId}) : super(key: key);
@override
_BookmarkIconState createState() => _BookmarkIconState();
}
class _BookmarkIconState extends State<BookmarkIcon> {
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(widget.inCollection.contains(widget.carId)
? Icons.bookmark
: Icons.bookmark_border),
onPressed: () {
if (widget.inCollection.contains(widget.carId)) {
widget.inCollection.remove(widget.carId);
} else {
widget.inCollection.add(widget.carId);
}
setState(() {
widget.inCollection = widget.inCollection;
});
},
);
}
}
import 'package:workshop_enterprise/models/hw_collection.dart';
class CollectionDetailArgs {
final MiniCollection miniCollection;
CollectionDetailArgs(this.miniCollection);
}
import 'dart:ui';
extension ColorFromHexString on String {
Color hexToColor() {
return Color(int.tryParse(this.replaceAll("#", "0xff")));
}
}
import 'package:dio/dio.dart';
class DioProviderBase {
static const String URL_BASE = "https://product.mattel.com/api/ProductInfo/";
static final DioProviderBase _dioProviderBase = DioProviderBase._internal();
DioProviderBase._internal();
final dio = Dio(
BaseOptions(
baseUrl: URL_BASE,
connectTimeout: Duration(minutes: 3).inMilliseconds,
receiveTimeout: Duration(minutes: 5).inMilliseconds,
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
),
);
factory DioProviderBase() {
return _dioProviderBase;
}
Future<Response> get(String url, Map<String, dynamic> params) async {
return await dio.get(url, queryParameters: params ?? {});
}
Future<Response> post(String url, Map<String, dynamic> bodyData) async {
return await dio.post(url, data: bodyData ?? {});
}
}
import 'package:flutter/material.dart';
import 'package:workshop_enterprise/models/hw_collection.dart';
import 'package:workshop_enterprise/repositories/mini_collection_repository.dart';
import 'package:workshop_enterprise/screens/collection_details_args.dart';
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final collectionRepo = MiniCollectionRepository();
@override
Widget build(BuildContext context) {
return Material(
child: SafeArea(
child: Container(
child: FutureBuilder(
future: collectionRepo.get(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
continue loading;
break;
loading:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
break;
case ConnectionState.active:
// TODO: Handle this case.
break;
case ConnectionState.done:
return ListView.separated(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
final MiniCollection miniCollection =
snapshot.data[index];
return ListTile(
leading: SizedBox(
width: 40,
height: 40,
child: Image.network(miniCollection.imageURL),
),
trailing: Icon(Icons.chevron_right),
title: Text(miniCollection.name),
onTap: () {
return Navigator.pushNamed(
context,
'/collectionDetails',
arguments: CollectionDetailArgs(miniCollection),
);
},
);
},
separatorBuilder: (context, index) => Divider(),
);
break;
}
return Text("texto");
},
),
),
),
);
}
}
import 'package:json_annotation/json_annotation.dart';
part "mini_collection.g.dart";
@JsonSerializable()
class MiniCollection {
@JsonKey(name: 'Id')
int id;
@JsonKey(name: 'Name')
String name;
@JsonKey(name: 'BackGroundColor')
String bgColor;
@JsonKey(name: 'ForeGroundColor')
String fgColor;
@JsonKey(name: 'SEOName')
String seoName;
@JsonKey(name: 'TotalItems')
int totalItems;
@JsonKey(name: 'ImageUrl')
String imageURL;
MiniCollection({
this.id,
this.name,
this.bgColor,
this.fgColor,
this.seoName,
this.totalItems,
this.imageURL,
});
factory MiniCollection.fromJson(Map<String, dynamic> json) =>
_$MiniCollectionFromJson(json);
Map<String, dynamic> toJson() => _$MiniCollectionToJson(this);
}
import 'package:dio/dio.dart';
import 'package:workshop_enterprise/models/hw_collection.dart';
import 'package:workshop_enterprise/services/dio_provider_base.dart';
class MiniCollectionRepository {
static const String URL_GET_COLLECTIONS = "GetAllMiniCollection";
Dio dio = DioProviderBase().dio;
Future<List<MiniCollection>> get() async {
final _miniCollections = List<MiniCollection>();
Response response = await dio.get(URL_GET_COLLECTIONS);
if (response.statusCode == 200) {
List.castFrom(response.data).forEach(
(element) => _miniCollections.add(MiniCollection.fromJson(element)),
);
return _miniCollections;
}
return null;
}
}
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.3
dio: ^3.0.10
json_annotation: ^3.0.1
sqflite: ^1.3.1
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.10.0
json_serializable: ^3.3.0
import 'package:flutter/material.dart';
import 'package:workshop_enterprise/screens/collection_details.dart';
import 'package:workshop_enterprise/screens/collection_details_args.dart';
import 'package:workshop_enterprise/screens/home_screen.dart';
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => HomeScreen(),
'/collectionDetails': (BuildContext context) {
final CollectionDetailArgs args = ModalRoute.of(context).settings.arguments;
return CollectionDetails(
miniCollection: args.miniCollection,
);
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment