refactor: generate model class from json

This commit is contained in:
Rongjian Zhang 2019-01-22 19:41:12 +08:00
parent bd515cbbb6
commit 263913f60b
12 changed files with 275 additions and 127 deletions

23
build.yaml Normal file
View File

@ -0,0 +1,23 @@
targets:
$default:
builders:
json_serializable:
options:
# Options configure how source code is generated for every
# `@JsonSerializable`-annotated class in the package.
#
# The default value for each is listed.
#
# For usage information, reference the corresponding field in
# `JsonSerializableGenerator`.
# any_map: false
# checked: false
# create_factory: true
# create_to_json: true
# disallow_unrecognized_keys: false
# explicit_to_json: false
field_rename: snake
# generate_to_json_function: true
# include_if_null: true
# nullable: true
# use_wrappers: false

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../common/event.dart';
import '../components/event.dart';
import '../utils.dart';
import 'dart:async';

View File

@ -1,4 +1,5 @@
import '../utils.dart';
import '../models/event.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
@ -38,30 +39,92 @@ class EventItem extends StatelessWidget {
final Event event;
EventItem(this.event);
getEventItemByType() {
Widget getEventItemByType(BuildContext context) {
switch (event.type) {
case 'IssuesEvent':
return IssuesEvent(event);
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} issue '),
_strong(event.repo.name),
TextSpan(
text: '#' + event.payload['issue']['number'].toString(),
),
TextSpan(
text: event.payload['issue']['title'],
)
],
),
);
case 'PushEvent':
return PushEvent(event);
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' pushed to '),
TextSpan(
text: event.payload['ref'],
style: TextStyle(color: CupertinoColors.activeBlue),
),
TextSpan(text: ' in '),
_strong(event.repo.name),
TextSpan(text: '')
],
),
);
case 'PullRequestEvent':
return PullRequestEvent(event);
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} pull request '),
_strong(event.repo.name),
TextSpan(text: '#' + event.payload['number'].toString()),
TextSpan(text: event.payload['pull_request']['title'])
],
),
);
case 'WatchEvent':
return WatchEvent(event);
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} '),
_strong(event.repo.name),
],
),
);
default:
return Text('Not implement yet');
return Text(
'Not implement yet',
style: TextStyle(color: CupertinoColors.destructiveRed),
);
}
}
@override
build(context) {
// Padding(padding: EdgeInsets.only(top: 16.0)),
return Row(
children: [
_Avatar(event.avatar),
Expanded(child: getEventItemByType()),
],
return Container(
padding: EdgeInsets.only(top: 16.0),
decoration: BoxDecoration(
border: Border(
top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
right: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
),
),
child: Row(
children: [
_Avatar(event.actor.avatarUrl),
Expanded(child: getEventItemByType(context)),
],
),
);
}
}
@ -79,7 +142,7 @@ TextSpan _strong(String text, [GestureRecognizer recognizer]) {
TextSpan _user(Event event, context) {
return _strong(
event.actor,
event.actor.login,
// TapGestureRecognizer()
// ..onTap = () {
// Navigator.of(context).push(
@ -93,31 +156,6 @@ TextSpan _user(Event event, context) {
);
}
class PushEvent extends StatelessWidget {
final Event event;
PushEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' pushed to '),
TextSpan(
text: event.payload['ref'],
style: TextStyle(color: CupertinoColors.activeBlue),
),
TextSpan(text: ' in '),
_strong(event.repo),
TextSpan(text: '')
],
),
);
}
}
class IssuesEvent extends StatelessWidget {
final Event event;
IssuesEvent(this.event);
@ -130,7 +168,7 @@ class IssuesEvent extends StatelessWidget {
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} issue '),
_strong(event.repo),
_strong(event.repo.name),
TextSpan(
text: '#' + event.payload['issue']['number'].toString(),
),
@ -143,27 +181,6 @@ class IssuesEvent extends StatelessWidget {
}
}
class PullRequestEvent extends StatelessWidget {
final Event event;
PullRequestEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} pull request '),
_strong(event.repo),
TextSpan(text: '#' + event.payload['number'].toString()),
TextSpan(text: event.payload['pull_request']['title'])
],
),
);
}
}
class IssueCommentEvent extends StatelessWidget {
final Event event;
IssueCommentEvent(this.event);
@ -176,7 +193,7 @@ class IssueCommentEvent extends StatelessWidget {
children: [
_user(event, context),
TextSpan(text: ' commented on issue '),
_strong(event.repo),
_strong(event.repo.name),
TextSpan(text: '#' + event.payload['issue']['number'].toString()),
TextSpan(text: event.payload['comment']['body'])
],
@ -184,22 +201,3 @@ class IssueCommentEvent extends StatelessWidget {
);
}
}
class WatchEvent extends StatelessWidget {
final Event event;
WatchEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} '),
_strong(event.repo),
],
),
);
}
}

View File

@ -2,10 +2,12 @@
// import 'dart:convert';
import '../utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
// import 'package:graphql_flutter/graphql_flutter.dart';
// import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import '../common/event.dart';
import 'user.dart';
import '../components/event.dart';
// import 'user.dart';
import '../models/event.dart';
class IosHomeTab extends StatefulWidget {
@override
@ -32,7 +34,7 @@ class IosHomeTabState extends State<IosHomeTab> {
}
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
GlobalKey<RefreshIndicatorState>();
@override
Widget build(context) {
@ -60,7 +62,7 @@ class IosHomeTabState extends State<IosHomeTab> {
try {
return EventItem(events[index]);
} catch (err) {
return Text(err.toString());
return Text(err.toString());
// return null;
}
});

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../models/user.dart';
import '../utils.dart';
class IosUserPage extends StatelessWidget {
@ -12,7 +13,7 @@ class IosUserPage extends StatelessWidget {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: CupertinoButton(
child: Text('Cancel'),
child: Text('Cancel'),
padding: EdgeInsets.zero,
onPressed: () {
Navigator.of(context).pop(false);

View File

@ -1,9 +1,22 @@
import 'package:flutter/material.dart';
// import 'dart:io';
// import 'package:graphql_flutter/graphql_flutter.dart';
import 'android/main.dart';
import 'ios/main.dart';
// import 'token.dart';
class App extends StatelessWidget {
final isIos = false;
final isIos = true;
// final ValueNotifier<GraphQLClient> client = ValueNotifier(
// GraphQLClient(
// cache: InMemoryCache(),
// link: HttpLink(
// uri: 'https://api.github.com/graphql',
// headers: {HttpHeaders.authorizationHeader: 'token $token'},
// ),
// ),
// );
@override
build(context) {

38
lib/models/event.dart Normal file
View File

@ -0,0 +1,38 @@
import 'package:json_annotation/json_annotation.dart';
part 'event.g.dart';
@JsonSerializable()
class Actor {
Actor(this.login, this.avatarUrl);
String login;
String avatarUrl;
factory Actor.fromJson(Map<String, dynamic> json) => _$ActorFromJson(json);
Map<String, dynamic> toJson() => _$ActorToJson(this);
}
@JsonSerializable()
class Repo {
Repo(this.name);
String name;
factory Repo.fromJson(Map<String, dynamic> json) => _$RepoFromJson(json);
Map<String, dynamic> toJson() => _$RepoToJson(this);
}
@JsonSerializable()
class Event {
Event(this.id, this.type, this.actor, this.repo);
String id;
String type;
Actor actor;
Repo repo;
Map<String, dynamic> payload;
factory Event.fromJson(Map<String, dynamic> json) => _$EventFromJson(json);
Map<String, dynamic> toJson() => _$EventToJson(this);
}

44
lib/models/event.g.dart Normal file
View File

@ -0,0 +1,44 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'event.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Actor _$ActorFromJson(Map<String, dynamic> json) {
return Actor(json['login'] as String, json['avatar_url'] as String);
}
Map<String, dynamic> _$ActorToJson(Actor instance) => <String, dynamic>{
'login': instance.login,
'avatar_url': instance.avatarUrl
};
Repo _$RepoFromJson(Map<String, dynamic> json) {
return Repo(json['name'] as String);
}
Map<String, dynamic> _$RepoToJson(Repo instance) =>
<String, dynamic>{'name': instance.name};
Event _$EventFromJson(Map<String, dynamic> json) {
return Event(
json['id'] as String,
json['type'] as String,
json['actor'] == null
? null
: Actor.fromJson(json['actor'] as Map<String, dynamic>),
json['repo'] == null
? null
: Repo.fromJson(json['repo'] as Map<String, dynamic>))
..payload = json['payload'] as Map<String, dynamic>;
}
Map<String, dynamic> _$EventToJson(Event instance) => <String, dynamic>{
'id': instance.id,
'type': instance.type,
'actor': instance.actor,
'repo': instance.repo,
'payload': instance.payload
};

31
lib/models/user.dart Normal file
View File

@ -0,0 +1,31 @@
import 'package:json_annotation/json_annotation.dart';
/// This allows the `User` class to access private members in
/// the generated file. The value for this is *.g.dart, where
/// the star denotes the source file name.
part 'user.g.dart';
/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
@JsonSerializable()
class User {
User(this.login, this.avatarUrl, this.name, this.publicRepos, this.followers,
this.following);
String login;
String avatarUrl;
String name;
int publicRepos;
int followers;
int following;
/// A necessary factory constructor for creating a new User instance
/// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
/// The constructor is named after the source class, in this case User.
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$UserToJson`.
Map<String, dynamic> toJson() => _$UserToJson(this);
}

26
lib/models/user.g.dart Normal file
View File

@ -0,0 +1,26 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
User _$UserFromJson(Map<String, dynamic> json) {
return User(
json['login'] as String,
json['avatar_url'] as String,
json['name'] as String,
json['public_repos'] as int,
json['followers'] as int,
json['following'] as int);
}
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
'login': instance.login,
'avatar_url': instance.avatarUrl,
'name': instance.name,
'public_repos': instance.publicRepos,
'followers': instance.followers,
'following': instance.following
};

View File

@ -3,50 +3,18 @@ import 'dart:async';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'token.dart';
import 'models/event.dart';
import 'models/user.dart';
final prefix = 'https://api.github.com';
class Event {
String type;
String actor;
String id;
String avatar;
String repo;
Map<String, dynamic> payload;
Event.fromJson(data) {
id = data['id'];
type = data['type'];
actor = data['actor']['login'];
avatar = data['actor']['avatar_url'];
repo = data['repo']['name'];
payload = data['payload'];
}
}
class User {
String login;
String avatar;
String name;
int repos;
int followers;
int following;
User.fromJson(data) {
login = data['login'];
avatar = data['avatar_url'];
name = data['name'];
repos = data['public_repos'];
followers = data['followers'];
following = data['following'];
}
}
Future<List<Event>> fetchEvents([int page = 1]) async {
final res = await http.get(
prefix + '/users/pd4d10/received_events/public?page=$page',
headers: {HttpHeaders.AUTHORIZATION: 'token $token'},
headers: {HttpHeaders.authorizationHeader: 'token $token'},
);
print(res.body);
List<dynamic> data = json.decode(res.body);
return data.map((item) {
@ -57,7 +25,7 @@ Future<List<Event>> fetchEvents([int page = 1]) async {
Future<User> fetchUser(String login) async {
final res = await http.get(
prefix + '/users/$login',
headers: {HttpHeaders.AUTHORIZATION: 'token $token'},
headers: {HttpHeaders.authorizationHeader: 'token $token'},
);
Map<String, dynamic> data = json.decode(res.body);

View File

@ -20,11 +20,15 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
http: ^0.11.3
graphql_flutter: ^1.0.0-alpha
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.1.3
json_serializable: ^2.0.1
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec