├── android
├── settings_aar.gradle
├── gradle.properties
├── .gitignore
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable
│ │ │ │ │ ├── soccer_animation.png
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ └── values
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── efhem
│ │ │ │ │ └── football
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── local.properties
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── build.gradle
├── lib
├── config
│ ├── config.dart
│ ├── theme.dart
│ ├── styles.dart
│ └── palette.dart
├── viewmodel
│ ├── provider.dart
│ ├── base_viewmodel.dart
│ ├── news_provider.dart
│ └── football_provider.dart
├── model
│ ├── user.dart
│ ├── onboarding.dart
│ ├── league.dart
│ ├── news.dart
│ ├── team.dart
│ ├── table_item.dart
│ └── match.dart
├── widgets
│ ├── widget.dart
│ ├── list_header.dart
│ ├── round_image.dart
│ ├── team_item.dart
│ ├── sliver_custom_appbar.dart
│ ├── custom_search_bar.dart
│ ├── news_item.dart
│ ├── live_match_item.dart
│ └── match_item.dart
├── data
│ ├── datasource
│ │ ├── datasource.dart
│ │ ├── footbal
│ │ │ ├── news_remote_datasource.dart
│ │ │ ├── football_remote_datasource.dart
│ │ │ ├── football_remote_data_source_imp.dart
│ │ │ └── football_local_data_source.dart
│ │ ├── secret_loader.dart
│ │ └── news
│ │ │ ├── news_remote_data_source.dart
│ │ │ └── news_local_data_source.dart
│ ├── model
│ │ ├── news_response.dart
│ │ ├── articles_remote.dart
│ │ ├── articles_local.dart
│ │ ├── news_response.g.dart
│ │ ├── articles_remote.g.dart
│ │ ├── articles_local.g.dart
│ │ ├── league_table_local.dart
│ │ ├── league_table_local.g.dart
│ │ ├── games_local.dart
│ │ ├── games_local.g.dart
│ │ ├── games_response.dart
│ │ └── table_response.dart
│ ├── hive
│ │ └── hive.dart
│ ├── helper
│ │ ├── network_helper.dart
│ │ └── server_error.dart
│ ├── retrofit
│ │ ├── api_service.dart
│ │ └── api_service.g.dart
│ ├── repository
│ │ ├── news_repository_imp.dart
│ │ └── football_repository_imp.dart
│ └── data.dart
├── domain
│ ├── repository
│ │ ├── news_repository.dart
│ │ └── football_repository.dart
│ ├── domain.dart
│ ├── model
│ │ ├── failure.dart
│ │ └── secret.dart
│ └── util
│ │ └── utils.dart
├── screens
│ ├── screen.dart
│ ├── bookmark_screen.dart
│ ├── home
│ │ ├── search_screen.dart
│ │ ├── league_table_screen.dart
│ │ ├── football_screen.dart
│ │ └── football_details.dart
│ ├── news
│ │ ├── news_details.dart
│ │ └── news_screen.dart
│ ├── home_screen.dart
│ ├── profile_screen.dart
│ └── onboarding.dart
├── utils
│ ├── storage_util.dart
│ └── custom_route.dart
├── injection_container.dart
└── main.dart
├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ │ └── LaunchImage.imageset
│ │ │ ├── soccer_animation.png
│ │ │ ├── soccer_animation-1.png
│ │ │ ├── soccer_animation-2.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── .gitignore
├── Podfile
└── Podfile.lock
├── screenshots
└── football.png
├── .metadata
├── .gitignore
├── README.md
├── pubspec.yaml
└── pubspec.lock
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/lib/config/config.dart:
--------------------------------------------------------------------------------
1 | export 'styles.dart';
2 | export 'palette.dart';
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/screenshots/football.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/screenshots/football.png
--------------------------------------------------------------------------------
/lib/viewmodel/provider.dart:
--------------------------------------------------------------------------------
1 | export 'news_provider.dart';
2 | export 'base_viewmodel.dart';
3 | export 'football_provider.dart';
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/soccer_animation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/android/app/src/main/res/drawable/soccer_animation.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/soccer_animation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/soccer_animation.png
--------------------------------------------------------------------------------
/lib/model/user.dart:
--------------------------------------------------------------------------------
1 |
2 | class User {
3 | final String avatar;
4 | final String name;
5 | final String nickName;
6 |
7 | const User({this.avatar, this.name, this.nickName});
8 | }
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/soccer_animation-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/soccer_animation-1.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/soccer_animation-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Efhemo/football/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/soccer_animation-2.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/efhem/football/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.efhem.football
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/lib/model/onboarding.dart:
--------------------------------------------------------------------------------
1 |
2 | class OnBoarding{
3 | final String imageAsset;
4 | final String headerTitle;
5 | final String subHeaderTitle;
6 |
7 | OnBoarding(this.imageAsset, this.headerTitle, this.subHeaderTitle);
8 |
9 | }
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/widgets/widget.dart:
--------------------------------------------------------------------------------
1 | export 'sliver_custom_appbar.dart';
2 | export 'custom_search_bar.dart';
3 | export 'list_header.dart';
4 | export 'round_image.dart';
5 | export 'live_match_item.dart';
6 | export 'match_item.dart';
7 | export 'news_item.dart';
--------------------------------------------------------------------------------
/lib/data/datasource/datasource.dart:
--------------------------------------------------------------------------------
1 | export 'package:football/data/datasource/news/news_local_data_source.dart';
2 | export 'package:football/data/datasource/news/news_remote_data_source.dart';
3 | export 'package:football/data/datasource/secret_loader.dart';
--------------------------------------------------------------------------------
/lib/domain/repository/news_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/domain/domain.dart';
2 | import 'package:football/model/news.dart';
3 |
4 | abstract class NewsRepository {
5 | Future>> fetchNews();
6 |
7 | Stream watchNews();
8 | }
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
7 |
--------------------------------------------------------------------------------
/lib/data/datasource/footbal/news_remote_datasource.dart:
--------------------------------------------------------------------------------
1 | import 'package:dartz/dartz.dart';
2 | import 'package:football/domain/domain.dart';
3 | import 'package:football/data/data.dart';
4 |
5 | abstract class NewsRemoteDataSource {
6 | Future> sport (String apiKey);
7 | }
--------------------------------------------------------------------------------
/lib/domain/domain.dart:
--------------------------------------------------------------------------------
1 | export 'package:football/domain/repository/news_repository.dart';
2 | export 'package:football/domain/repository/football_repository.dart';
3 | export 'package:football/domain/model/failure.dart';
4 | export 'package:football/domain/util/utils.dart';
5 | export 'package:dartz/dartz.dart';
6 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/domain/model/failure.dart:
--------------------------------------------------------------------------------
1 |
2 | class Failure {
3 | final String status;
4 | final String message;
5 |
6 | Failure({this.status = "fail", this.message});
7 |
8 | factory Failure.fromJson(Map json){
9 | return Failure(status: json['status'], message: json['message']);
10 | }
11 | }
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/domain/model/secret.dart:
--------------------------------------------------------------------------------
1 |
2 | class Secret {
3 | final String newsApiKey;
4 | final String footballApiKey;
5 |
6 | Secret({this.newsApiKey, this.footballApiKey});
7 |
8 | factory Secret.fromJson(Map json) =>
9 | Secret(newsApiKey: json['news_api_key'], footballApiKey: json["football_api_key"]);
10 | }
--------------------------------------------------------------------------------
/lib/screens/screen.dart:
--------------------------------------------------------------------------------
1 | export 'home/football_screen.dart';
2 | export 'profile_screen.dart';
3 | export 'onboarding.dart';
4 | export 'home_screen.dart';
5 | export 'home/search_screen.dart';
6 | export 'home/football_details.dart';
7 | export 'home/league_table_screen.dart';
8 | export 'news/news_screen.dart';
9 | export 'bookmark_screen.dart';
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: b041144f833e05cf463b8887fa12efdec9493488
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Mon Oct 19 10:21:41 WAT 2020
8 | sdk.dir=/Users/efhem/Library/Android/sdk
9 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/lib/data/datasource/footbal/football_remote_datasource.dart:
--------------------------------------------------------------------------------
1 | import 'package:dartz/dartz.dart';
2 | import 'package:football/data/model/games_response.dart';
3 | import 'package:football/domain/domain.dart';
4 | import 'package:football/data/data.dart';
5 |
6 | abstract class FootballRemoteDataSource {
7 | Future> table (int leagueId);
8 | Future> games (int leagueId);
9 | }
--------------------------------------------------------------------------------
/lib/config/theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/utils/storage_util.dart';
2 | import 'package:theme_mode_handler/theme_mode_manager_interface.dart';
3 |
4 | class AppTheme extends IThemeModeManager with StorageUtil {
5 |
6 | @override
7 | Future loadThemeMode() async {
8 | return theme;
9 | }
10 |
11 | @override
12 | Future saveThemeMode(String value) async {
13 | return setTheme(value);
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/lib/screens/bookmark_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:football/widgets/widget.dart';
3 |
4 | class BookmarkScreen extends StatelessWidget {
5 | @override
6 | Widget build(BuildContext context) {
7 | return Scaffold(
8 | body: CustomScrollView(
9 | slivers: [
10 | SliverCustomAppBar(flexibleTitle: "Bookmark"),
11 | ],
12 | ),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/model/league.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | class League {
4 | final int id;
5 | final String area;
6 | final String name;
7 | final String code;
8 | final String emblemUrl;
9 | final int currentMatchday;
10 | final String startDate;
11 | final String endDate;
12 |
13 | League( {@required this.id, this.area, @required this.name, this.code,
14 | @required this.emblemUrl, @required this.currentMatchday, this.startDate, this.endDate});
15 |
16 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/config/styles.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Styles {
4 |
5 | // static const textTheme = TextTheme(
6 | // headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
7 | // headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
8 | // bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
9 | // );
10 |
11 | static const appBarTheme = AppBarTheme(
12 | color: Colors.transparent,
13 | brightness: Brightness.light,
14 | elevation: 0,
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "soccer_animation-1.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "soccer_animation.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "soccer_animation-2.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/data/model/news_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 | import 'articles_remote.dart';
3 |
4 | part 'news_response.g.dart';
5 |
6 | @JsonSerializable()
7 | class NewsResponse {
8 | String status;
9 | int totalResults;
10 | List articles;
11 |
12 | NewsResponse({this.status, this.totalResults, this.articles});
13 |
14 | factory NewsResponse.fromJson(Map json) =>
15 | _$NewsResponseFromJson(json);
16 |
17 | Map toJson() => _$NewsResponseToJson(this);
18 | }
19 |
--------------------------------------------------------------------------------
/lib/domain/repository/football_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/domain/domain.dart';
2 | import 'package:football/model/match.dart';
3 | import 'package:football/model/table_item.dart';
4 | import 'package:football/model/team.dart';
5 |
6 | abstract class FootballRepository {
7 | Future> fetchTable(int leagueId);
8 |
9 | Future>> fetchGames(int leagueId);
10 |
11 | List getLeagueTeam(int leagueId);
12 |
13 | List getLeagueTable(int leagueId);
14 |
15 | List topTeams();
16 | }
--------------------------------------------------------------------------------
/lib/data/datasource/secret_loader.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async' show Future;
2 | import 'dart:convert' show json;
3 | import 'package:flutter/services.dart' show rootBundle;
4 | import 'package:football/domain/model/secret.dart';
5 |
6 |
7 | class SecretLoader {
8 | final String secretPath;
9 |
10 | SecretLoader({this.secretPath});
11 |
12 | Future load() {
13 | return rootBundle.loadStructuredData(this.secretPath,
14 | (jsonStr) async {
15 | final secret = Secret.fromJson(json.decode(jsonStr));
16 | return secret;
17 | });
18 | }
19 | }
--------------------------------------------------------------------------------
/lib/model/news.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/model/articles_local.dart';
2 |
3 | class News {
4 | final int id;
5 | final String author;
6 | final String title;
7 | final String description;
8 | final String url;
9 | final String urlToImage;
10 | final String publishAt;
11 |
12 | News(this.id, this.author, this.title, this.description, this.url, this.urlToImage, this.publishAt);
13 |
14 | static News fromArticle(int key, ArticleLocal article){
15 | return News(key, article.author, article.title,
16 | article.description, article.url, article.urlToImage,
17 | article.publishedAt);
18 | }
19 | }
--------------------------------------------------------------------------------
/lib/data/datasource/news/news_remote_data_source.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:football/data/helper/network_helper.dart';
3 | import 'package:football/data/retrofit/api_service.dart';
4 | import 'package:football/domain/domain.dart';
5 | import 'package:football/data/data.dart';
6 |
7 |
8 | class NewsRemoteDataSourceImpl with NewsRemoteDataSource {
9 | final ApiService apiService;
10 |
11 | NewsRemoteDataSourceImpl({@required this.apiService});
12 |
13 | @override
14 | Future> sport(String apiKey) async {
15 | return await safeApiResult(apiService.news(apiKey: apiKey));
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/lib/domain/util/utils.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:intl/intl.dart';
4 |
5 | class Utils {
6 |
7 | static String dateFormat(DateTime dateTime){
8 | return "${dateTime.year}-${dateTime.month.toString().padLeft(2,'0')}-${dateTime.day.toString().padLeft(2,'0')}";
9 | }
10 |
11 | static String toAppDate(String date) {
12 | final parsedDate = DateTime.tryParse(date);
13 | return parsedDate != null ? DateFormat('dd MMM').format(parsedDate) : "";
14 | }
15 |
16 | static String toAppTime(String date) {
17 | final parsedDate = DateTime.tryParse(date);
18 | return parsedDate != null ? DateFormat('HH:mm').format(parsedDate) : "";
19 | }
20 | }
--------------------------------------------------------------------------------
/lib/data/model/articles_remote.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'articles_remote.g.dart';
4 |
5 | @JsonSerializable()
6 | class Article {
7 | String author;
8 | String title;
9 | String description;
10 | String url;
11 | String urlToImage;
12 | String publishedAt;
13 |
14 | Article(
15 | {this.author,
16 | this.title,
17 | this.description,
18 | this.url,
19 | this.urlToImage,
20 | this.publishedAt});
21 |
22 | factory Article.fromJson(Map json) =>
23 | _$ArticleFromJson(json);
24 |
25 | Map toJson() => _$ArticleToJson(this);
26 | }
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The Flutter Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | include ':app'
6 |
7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
8 | def properties = new Properties()
9 |
10 | assert localPropertiesFile.exists()
11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
12 |
13 | def flutterSdkPath = properties.getProperty("flutter.sdk")
14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
16 |
--------------------------------------------------------------------------------
/lib/model/team.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:football/data/model/league_table_local.dart';
3 |
4 | class Team {
5 | final int id;
6 | final String leauge;
7 | final String name;
8 | final String code;
9 | final String emblemUrl;
10 | final int position;
11 |
12 | Team({ @required this.id, @required this.leauge, @required this.name, this.code, @required this.emblemUrl, this.position});
13 |
14 | static Team fromTeamLocal(LeagueTableLocal leagueTableLocal) =>
15 | Team(id: leagueTableLocal.teamId, leauge: leagueTableLocal.leagueName,
16 | name: leagueTableLocal.teamName, emblemUrl: leagueTableLocal.teamLogo,
17 | position: leagueTableLocal.position);
18 | }
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.4'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/model/table_item.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:football/data/model/league_table_local.dart';
4 |
5 | class TableItem {
6 | final int id;
7 | final int position;
8 | final String avatar;
9 | final String name;
10 | final int point;
11 | final int gamePlayed;
12 | final int goalDifference;
13 |
14 | TableItem({@required this.id, this.position, this.avatar, this.name, this.point,
15 | this.gamePlayed, this.goalDifference});
16 |
17 | static TableItem fromTeamLocal(LeagueTableLocal lt) =>
18 | TableItem(id: lt.id, position: lt.position, avatar: lt.teamLogo, name: lt.teamName,
19 | point: lt.points, gamePlayed: lt.playedGames, goalDifference: lt.goalDifference);
20 | }
--------------------------------------------------------------------------------
/lib/screens/home/search_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:football/widgets/widget.dart';
3 |
4 | class SearchScreen extends StatelessWidget {
5 | @override
6 | Widget build(BuildContext context) {
7 | return Scaffold(
8 | body: SafeArea(
9 | child: Column(
10 | children: [
11 | Padding(
12 | padding: EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 5),
13 | child: CustomSearchBar (
14 | canStartSearch: true,
15 | showCursor: true,
16 | readOnly: false,
17 | ),
18 | ),
19 | Divider(thickness: 1.0)
20 | ],
21 | ),
22 | ),
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/data/datasource/news/news_local_data_source.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/hive/hive.dart';
2 | import 'package:football/data/model/articles_local.dart';
3 | import 'package:hive/hive.dart';
4 |
5 | class NewsLocalDataSourceImpl {
6 | final Box _articleBox = Hive.box(HiveSetup.Article);
7 |
8 | void saveArticle(ArticleLocal article) {
9 | Future.value([_articleBox.add(article)]);
10 | }
11 |
12 | Future> saveArticles(List articles) async {
13 | final listOfKeys = await _articleBox.addAll(articles);
14 | return listOfKeys.toList();
15 | }
16 |
17 | Stream watchArticle() => _articleBox.watch();
18 |
19 |
20 | // Removes all entries from the box.
21 | Future deleteAll() async {
22 | return await _articleBox.clear();
23 | }
24 | }
--------------------------------------------------------------------------------
/lib/data/hive/hive.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/model/articles_local.dart';
2 | import 'package:football/data/model/games_local.dart';
3 | import 'package:football/data/model/league_table_local.dart';
4 | import 'package:hive/hive.dart';
5 |
6 | class HiveSetup {
7 |
8 | static Future init() async {
9 |
10 | Hive.registerAdapter(ArticleLocalAdapter(), 0);
11 | await Hive.openBox(HiveSetup.Article);
12 |
13 | Hive.registerAdapter(LeagueTableLocalAdapter(), 1);
14 | await Hive.openBox(HiveSetup.LeagueTeam);
15 |
16 | Hive.registerAdapter(GamesLocalAdapter(), 2);
17 | await Hive.openBox(HiveSetup.Games);
18 | }
19 |
20 | static const Article = "articleLocal";
21 | static const LeagueTeam = "leagueTeam";
22 | static const Games = "games";
23 |
24 | }
--------------------------------------------------------------------------------
/lib/viewmodel/base_viewmodel.dart:
--------------------------------------------------------------------------------
1 | import 'package:dartz/dartz.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:football/domain/model/failure.dart';
4 | import 'package:football/model/news.dart';
5 |
6 | enum ViewState { initial, loading, loaded }
7 |
8 | class BaseViewModel extends ChangeNotifier {
9 |
10 | ViewState _state = ViewState.initial;
11 |
12 | ViewState get
13 | state => _state;
14 | void setViewState(ViewState state, {Failure failure}) {
15 | _state = state;
16 | notifyListeners();
17 | }
18 |
19 | T _result;
20 | T get result => _result;
21 | void setResult(T result) {
22 | _result = result;
23 | notifyListeners();
24 | }
25 |
26 | Failure _errorData;
27 | Failure get failure => _errorData;
28 | void setError(Failure errorData) {
29 | _errorData = errorData;
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/lib/data/helper/network_helper.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:dartz/dartz.dart';
3 | import 'package:football/domain/model/failure.dart';
4 | import 'package:football/data/helper/server_error.dart';
5 | import 'package:football/domain/domain.dart';
6 |
7 |
8 | //consider this boilerplate if this implementation does not work perfectly
9 | //https://dev.to/ashishrawat2911/handling-network-calls-and-exceptions-in-flutter-54me
10 |
11 | Future> safeApiResult(Future call) async {
12 |
13 | try {
14 | final result = await call;
15 | return Right(result);
16 | } on SocketException {
17 | return Left(Failure(message: "No Internet connection"));
18 | }
19 | catch (error) {
20 | final errorBody = ServerError.withError(error: error);
21 | return Left(Failure(message: errorBody.getErrorMessage()));
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/data/model/articles_local.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/model/articles_remote.dart';
2 | import 'package:hive/hive.dart';
3 |
4 | part 'articles_local.g.dart';
5 |
6 | @HiveType()
7 | class ArticleLocal {
8 | @HiveField(0)
9 | String author;
10 | @HiveField(1)
11 | String title;
12 | @HiveField(2)
13 | String description;
14 | @HiveField(3)
15 | String url;
16 | @HiveField(4)
17 | String urlToImage;
18 | @HiveField(5)
19 | String publishedAt;
20 |
21 | ArticleLocal(
22 | this.author,
23 | this.title,
24 |
25 | this.description,
26 | this.url,
27 | this.urlToImage,
28 | this.publishedAt);
29 |
30 | static ArticleLocal fromArticle(Article article){
31 | return ArticleLocal(article.author, article.title,
32 | article.description, article.url, article.urlToImage,
33 | article.publishedAt);
34 | }
35 | }
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/data/model/news_response.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'news_response.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | NewsResponse _$NewsResponseFromJson(Map json) {
10 | return NewsResponse(
11 | status: json['status'] as String,
12 | totalResults: json['totalResults'] as int,
13 | articles: (json['articles'] as List)
14 | ?.map((e) =>
15 | e == null ? null : Article.fromJson(e as Map))
16 | ?.toList(),
17 | );
18 | }
19 |
20 | Map _$NewsResponseToJson(NewsResponse instance) =>
21 | {
22 | 'status': instance.status,
23 | 'totalResults': instance.totalResults,
24 | 'articles': instance.articles,
25 | };
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 | test/
33 | .last_build_id
34 |
35 | # Web related
36 | lib/generated_plugin_registrant.dart
37 |
38 | # Symbolication related
39 | app.*.symbols
40 |
41 | # Obfuscation related
42 | app.*.map.json
43 |
44 | # Exceptions to above rules.
45 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
46 |
47 | .g.dart
48 | secrets.json
49 |
--------------------------------------------------------------------------------
/lib/widgets/list_header.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ListHeader extends StatelessWidget {
4 | final String headerTitle;
5 | final String trailingTitle;
6 | final Function onTrailingTap;
7 |
8 | const ListHeader({Key key, @required this.headerTitle, this.onTrailingTap, this.trailingTitle}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Row(
13 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
14 | children: [
15 | Text(headerTitle,
16 | style: TextStyle(
17 | fontWeight: FontWeight.w900, fontSize: 16.0)),
18 | trailingTitle != null ? InkWell(
19 | onTap: onTrailingTap,
20 | child: Text(trailingTitle.toUpperCase(),
21 | style: TextStyle(
22 | fontWeight: FontWeight.w500, color: Colors.grey)),
23 | ): SizedBox.shrink()
24 | ],
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/data/model/articles_remote.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'articles_remote.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Article _$ArticleFromJson(Map json) {
10 | return Article(
11 | author: json['author'] as String,
12 | title: json['title'] as String,
13 | description: json['description'] as String,
14 | url: json['url'] as String,
15 | urlToImage: json['urlToImage'] as String,
16 | publishedAt: json['publishedAt'] as String,
17 | );
18 | }
19 |
20 | Map _$ArticleToJson(Article instance) => {
21 | 'author': instance.author,
22 | 'title': instance.title,
23 | 'description': instance.description,
24 | 'url': instance.url,
25 | 'urlToImage': instance.urlToImage,
26 | 'publishedAt': instance.publishedAt,
27 | };
28 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/utils/storage_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:shared_preferences/shared_preferences.dart';
2 |
3 | class StorageUtil {
4 |
5 | static Future _pref() async => await SharedPreferences.getInstance();
6 |
7 | get theme => _pref().then((value) => value.getString(_THEME_MODE));
8 | Future setTheme(String themeValue) {
9 | return _pref().then((value) => value.setString(_THEME_MODE, themeValue));
10 | }
11 |
12 | static setBool(String key, bool value) async {
13 | final pref = await _pref();
14 | pref.setBool(key, value);
15 | }
16 |
17 | static Future getBoolean(String key) async {
18 | final pref = await _pref();
19 | return pref.getBool(key) ?? false;
20 | }
21 |
22 | static setString(String key, String value) async {
23 | final pref = await _pref();
24 | pref.setString(key, value);
25 | }
26 |
27 | static Future getString(String key) async {
28 | final pref = await _pref();
29 | return pref.getString(key) ?? "";
30 | }
31 |
32 | static const _THEME_MODE = "theme_mode";
33 | static const HAS_BOARDED = "has_boarded";
34 |
35 | }
--------------------------------------------------------------------------------
/lib/data/datasource/footbal/football_remote_data_source_imp.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:football/data/helper/network_helper.dart';
3 | import 'package:football/data/model/games_response.dart';
4 | import 'package:football/data/retrofit/api_service.dart';
5 | import 'package:football/domain/domain.dart';
6 | import 'package:football/data/data.dart';
7 |
8 |
9 | class FootballRemoteDataSourceImpl with FootballRemoteDataSource {
10 | final ApiService apiService;
11 |
12 | FootballRemoteDataSourceImpl({@required this.apiService});
13 |
14 | @override
15 | Future> table (int leagueId) async {
16 | return await safeApiResult(apiService.standings(leagueId));
17 | }
18 |
19 | @override
20 | Future> games (int leagueId) async {
21 | final currentDate = DateTime.now();
22 | final yesterday = currentDate.subtract(Duration(days: 1));
23 | final next8Days = currentDate.add(Duration(days: 8));
24 |
25 | return await safeApiResult(apiService.games(
26 | leagueId,
27 | Utils.dateFormat(yesterday),
28 | Utils.dateFormat(next8Days)
29 | ));
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/lib/utils/custom_route.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 |
4 | class Util {
5 |
6 | static Route slideUpRoute(Widget route) {
7 | return PageRouteBuilder(
8 | pageBuilder: (context, animation, secondaryAnimation) => route,
9 | transitionDuration: Duration(milliseconds: 200),
10 | transitionsBuilder: (context, animation, secondaryAnimation, child) {
11 | var begin = Offset(0.0, 0.1);
12 | var end = Offset.zero;
13 | var curve = Curves.ease;
14 | var tween =
15 | Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
16 | return SlideTransition(position: animation.drive(tween), child: child);
17 | },
18 | );
19 | }
20 |
21 | static void showSnackBar(BuildContext context, String message, Function function) {
22 | Future.delayed(Duration(milliseconds: 500), (){
23 | Scaffold.of(context).showSnackBar(SnackBar(
24 | duration: Duration(days: 1),
25 | content: Text(message),
26 | action: SnackBarAction(
27 | label: "Retry",
28 | onPressed: () {
29 | Scaffold.of(context).hideCurrentSnackBar();
30 | function();
31 | }),
32 | ));
33 | });
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/data/datasource/footbal/football_local_data_source.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/hive/hive.dart';
2 | import 'package:football/data/model/games_local.dart';
3 | import 'package:football/data/model/league_table_local.dart';
4 | import 'package:hive/hive.dart';
5 |
6 | class FootballLocalDataSourceImpl {
7 |
8 | final Box _leagueTableBox = Hive.box(HiveSetup.LeagueTeam);
9 |
10 | void saveTeam(int teamId, LeagueTableLocal team){
11 | Future.value([_leagueTableBox.put(teamId, team)]);
12 | }
13 |
14 | Future saveTeams(List _teams) async {
15 | final Map teams = Map.fromIterable(_teams, key: (v) => v.teamId, value: (v) => v);
16 | return await _leagueTableBox.putAll(teams);
17 | }
18 |
19 | List getLeagueTeam(int leagueId) =>
20 | _leagueTableBox.values.where((element) => element.id == leagueId).toList();
21 |
22 | List topTeams() => _leagueTableBox.values.where((element) => element.position < 5).toList();
23 |
24 | String teamLogo(int teamId) {
25 | final leagueTable = _leagueTableBox.values.firstWhere((element) => element.teamId == teamId, orElse: null);
26 | return leagueTable != null ? leagueTable.teamLogo : "";
27 | }
28 | }
--------------------------------------------------------------------------------
/lib/data/model/articles_local.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'articles_local.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class ArticleLocalAdapter extends TypeAdapter {
10 | @override
11 | ArticleLocal read(BinaryReader reader) {
12 | var numOfFields = reader.readByte();
13 | var fields = {
14 | for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
15 | };
16 | return ArticleLocal(
17 | fields[0] as String,
18 | fields[1] as String,
19 | fields[2] as String,
20 | fields[3] as String,
21 | fields[4] as String,
22 | fields[5] as String,
23 | );
24 | }
25 |
26 | @override
27 | void write(BinaryWriter writer, ArticleLocal obj) {
28 | writer
29 | ..writeByte(6)
30 | ..writeByte(0)
31 | ..write(obj.author)
32 | ..writeByte(1)
33 | ..write(obj.title)
34 | ..writeByte(2)
35 | ..write(obj.description)
36 | ..writeByte(3)
37 | ..write(obj.url)
38 | ..writeByte(4)
39 | ..write(obj.urlToImage)
40 | ..writeByte(5)
41 | ..write(obj.publishedAt);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/data/retrofit/api_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/model/games_response.dart';
2 | import 'package:football/domain/domain.dart';
3 | import 'package:pretty_dio_logger/pretty_dio_logger.dart';
4 | import 'package:retrofit/http.dart';
5 | import 'package:dio/dio.dart';
6 | import 'package:retrofit/retrofit.dart';
7 | import 'package:football/data/data.dart';
8 |
9 | part 'api_service.g.dart';
10 |
11 | @RestApi(baseUrl: "https://api.football-data.org/v2/")
12 | abstract class ApiService {
13 | factory ApiService(Dio dio, {String baseUrl}) = _ApiService;
14 |
15 | static ApiService create( String footballApiKey) {
16 | final dio = Dio();
17 | dio.interceptors.add(PrettyDioLogger());
18 | dio.options.headers["X-Auth-Token"] = footballApiKey;
19 | return ApiService(dio);
20 | }
21 |
22 | @GET("http://newsapi.org/v2/top-headlines")
23 | Future news({
24 | @Query("category") String category = "sport",
25 | @Query("country") String country = "us",
26 | @Query("apiKey") String apiKey,
27 | });
28 |
29 | @GET("competitions/{id}/standings")
30 | Future standings ( @Path("id") int leagueId);
31 |
32 | @GET("competitions/{id}/matches")
33 | Future games (
34 | @Path("id") int leagueId,
35 | @Query("dateFrom") String dateFrom,
36 | @Query("dateTo") String dateTo
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/lib/widgets/round_image.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/svg.dart';
3 |
4 | class RoundImage extends StatelessWidget {
5 | final String imageUrl;
6 | final bool isElevated;
7 | final bool isSelected;
8 | final Function onTap;
9 |
10 | const RoundImage({Key key, @required this.imageUrl, this.isElevated = false, this.onTap, this.isSelected = false})
11 | : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return GestureDetector(
16 | onTap: onTap,
17 | child: Container(
18 | height: 70,
19 | decoration: BoxDecoration(
20 | border: isSelected ? Border.all(color: Colors.blueAccent, width: 1.5) : null,
21 | color: Colors.white,
22 | shape: BoxShape.circle,
23 | boxShadow: isElevated ? [
24 | BoxShadow(
25 | color: Colors.grey.withOpacity(0.2),
26 | spreadRadius: 5,
27 | blurRadius: 7,
28 | offset: Offset(0, 0.5) // changes position of shadow
29 | )
30 | ]: null,
31 | ),
32 | child: Container(
33 | padding: EdgeInsets.all(10.0),
34 | child: SvgPicture.network(
35 | imageUrl,
36 | width: 35.0,
37 | ),
38 | )),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/config/palette.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 |
4 | class Palette {
5 | static Color backgroundColor = Color(0xFFF8F8F8);
6 | static Color backgroundColorDark = Color(0xFF1D1B1C);
7 | static Color darkGrey = Color(0xFF202020);
8 | static Color faintBlue(ThemeMode themeMode){
9 | return themeMode == ThemeMode.dark ? Colors.grey : Color(0xFFEAEDF3);
10 | }
11 | static Color lightWhite = Color(0xFFEAEAEB);
12 |
13 | static Color appBarTitleColor(ThemeMode themeMode) {
14 | return themeMode == ThemeMode.dark ? Colors.white : Colors.black;
15 | }
16 | static Brightness appBarTitleBrightness(ThemeMode themeMode){
17 | return themeMode == ThemeMode.dark ? Brightness.dark : Brightness.light;
18 | }
19 |
20 | static void setUpStatusBarThemeMode(ThemeMode themeMode) {
21 | if (themeMode == ThemeMode.dark) {
22 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
23 | systemNavigationBarColor: Colors.black,
24 | statusBarColor: backgroundColorDark,
25 | //statusBarIconBrightness: Brightness.dark, // status bar icons' color
26 | //systemNavigationBarIconBrightness: Brightness.dark, //navigation bar icons' color
27 | ));
28 | } else {
29 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
30 | systemNavigationBarColor: Colors.black,
31 | statusBarColor: backgroundColor,
32 | ));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/viewmodel/news_provider.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:football/model/news.dart';
5 | import 'base_viewmodel.dart';
6 | import 'package:football/domain/domain.dart';
7 |
8 | class NewsProvider extends BaseViewModel {
9 | final NewsRepository newsRepository;
10 |
11 | StreamController> _streamController = StreamController();
12 | Stream> get newsStream => _streamController.stream;
13 |
14 | NewsProvider({@required this.newsRepository}){
15 | fetchNews();
16 |
17 | //watchNews();
18 | }
19 |
20 | //not working
21 | void watchNews() {
22 | var listNews = List();
23 |
24 | newsRepository.watchNews().listen((news) {
25 | listNews.add(news);
26 | print("news title is ${news.title}");
27 | }, onDone: () { // Not excecuting
28 | _streamController.sink.add(listNews);
29 | print("onDone is ${listNews.length}");
30 | listNews = List();
31 | }, onError: (error) {
32 | print("on error is ${error}");
33 | });
34 | }
35 |
36 | void fetchNews() async {
37 | setViewState(ViewState.loading);
38 | final result = await newsRepository.fetchNews();
39 | result.fold(
40 | (l) => setError(l),
41 | (r) => setError(null)
42 | );
43 | setViewState(ViewState.loaded);
44 | }
45 |
46 | @override
47 | void dispose() {
48 | _streamController.close();
49 | super.dispose();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/screens/news/news_details.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:football/config/palette.dart';
5 | import 'package:football/model/news.dart';
6 | import 'package:theme_mode_handler/theme_mode_handler.dart';
7 | import 'package:webview_flutter/webview_flutter.dart';
8 |
9 | class NewsDetailsScreen extends StatefulWidget {
10 |
11 | final News news;
12 | NewsDetailsScreen(this.news);
13 |
14 | @override
15 | State createState() {
16 | return NewsDetailsState();
17 | }
18 | }
19 |
20 | class NewsDetailsState extends State {
21 |
22 | @override
23 | void initState() {
24 | super.initState();
25 | // Enable hybrid composition.
26 | //if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
27 | }
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | final themeMode = ThemeModeHandler.of(context).themeMode;
32 | return Scaffold(
33 | appBar: AppBar(
34 | title: Text(widget.news.author ?? widget.news.title, style: TextStyle(color: Palette.appBarTitleColor(themeMode))),
35 | leading: GestureDetector(child: Icon(Icons.arrow_back_ios, color: Palette.appBarTitleColor(themeMode)),
36 | onTap: () { Navigator.pop(context); },),
37 | ),
38 | body: WebView(
39 | initialUrl: widget.news.url,
40 | javascriptMode: JavascriptMode.unrestricted,
41 | ),
42 | );
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - FMDB (2.7.5):
4 | - FMDB/standard (= 2.7.5)
5 | - FMDB/standard (2.7.5)
6 | - path_provider (0.0.1):
7 | - Flutter
8 | - shared_preferences (0.0.1):
9 | - Flutter
10 | - sqflite (0.0.1):
11 | - Flutter
12 | - FMDB (~> 2.7.2)
13 | - webview_flutter (0.0.1):
14 | - Flutter
15 |
16 | DEPENDENCIES:
17 | - Flutter (from `Flutter`)
18 | - path_provider (from `.symlinks/plugins/path_provider/ios`)
19 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
20 | - sqflite (from `.symlinks/plugins/sqflite/ios`)
21 | - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`)
22 |
23 | SPEC REPOS:
24 | trunk:
25 | - FMDB
26 |
27 | EXTERNAL SOURCES:
28 | Flutter:
29 | :path: Flutter
30 | path_provider:
31 | :path: ".symlinks/plugins/path_provider/ios"
32 | shared_preferences:
33 | :path: ".symlinks/plugins/shared_preferences/ios"
34 | sqflite:
35 | :path: ".symlinks/plugins/sqflite/ios"
36 | webview_flutter:
37 | :path: ".symlinks/plugins/webview_flutter/ios"
38 |
39 | SPEC CHECKSUMS:
40 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
41 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
42 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
43 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
44 | sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0
45 | webview_flutter: d2b4d6c66968ad042ad94cbb791f5b72b4678a96
46 |
47 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
48 |
49 | COCOAPODS: 1.9.1
50 |
--------------------------------------------------------------------------------
/lib/data/helper/server_error.dart:
--------------------------------------------------------------------------------
1 | import 'package:dio/dio.dart' hide Headers;
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:football/domain/model/failure.dart';
4 |
5 | class ServerError implements Exception {
6 | String _errorMessage = "";
7 |
8 | ServerError.withError({@required DioError error}) {
9 | _handleError(error);
10 | }
11 |
12 | getErrorMessage() {
13 | return _errorMessage;
14 | }
15 |
16 | _handleError(DioError error) {
17 | switch (error.type) {
18 | case DioErrorType.CANCEL:
19 | _errorMessage = "Request was cancelled";
20 | break;
21 | case DioErrorType.CONNECT_TIMEOUT:
22 | _errorMessage = "Connection timeout";
23 | break;
24 | case DioErrorType.DEFAULT:
25 | _errorMessage =
26 | "Connection failed due to internet connection";
27 | break;
28 | case DioErrorType.RECEIVE_TIMEOUT:
29 | _errorMessage = "Receive timeout in connection";
30 | break;
31 | case DioErrorType.RESPONSE:
32 | _errorMessage = "Unknown Error";
33 | final errorBody = extractErrorBody(error);
34 | if( errorBody != null){ _errorMessage = errorBody.message;}
35 | break;
36 | case DioErrorType.SEND_TIMEOUT:
37 | _errorMessage = "Receive timeout in send request";
38 | break;
39 | }
40 | return _errorMessage;
41 | }
42 |
43 | Failure extractErrorBody(DioError error){
44 | try {
45 | return Failure.fromJson(error.response.data);
46 | } catch (e){
47 | return null;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/lib/widgets/team_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:football/config/palette.dart';
3 | import 'package:football/widgets/round_image.dart';
4 | import 'package:theme_mode_handler/theme_mode_handler.dart';
5 |
6 | class TeamItem extends StatelessWidget {
7 |
8 | final String title;
9 | final String imageUrl;
10 | final String subtitle;
11 | final int id;
12 | final String trailing;
13 | final Function() onTap;
14 |
15 | const TeamItem({Key key, @required this.title, @required this.imageUrl, this.subtitle, @required this.id, this.trailing, this.onTap}) : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | final themeMode = ThemeModeHandler.of(context).themeMode;
20 | return InkWell(
21 | onTap: onTap,
22 | child: Padding(
23 | padding: const EdgeInsets.symmetric(horizontal: 8.0),
24 | child: ListTile(
25 | contentPadding: EdgeInsets.only(left: 10, top: 3, bottom: 3, right: 23.0),
26 | leading: RoundImage(imageUrl: imageUrl),
27 | title: Text(title, style: TextStyle(fontWeight: FontWeight.w900, fontSize: 15.0)),
28 | subtitle: Text(subtitle, style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold)),
29 | trailing: Container(
30 | padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 6.0),
31 | decoration: BoxDecoration(
32 | color: Palette.faintBlue(themeMode),
33 | borderRadius: BorderRadius.circular(5.0)
34 | ),
35 | child: Text(trailing, style: TextStyle(fontWeight: FontWeight.bold)),
36 | )
37 | )
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/data/repository/news_repository_imp.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:football/data/datasource/datasource.dart';
4 | import 'package:football/data/datasource/news/news_local_data_source.dart';
5 | import 'package:football/domain/domain.dart';
6 | import 'package:football/data/model/articles_local.dart';
7 | import 'package:football/model/news.dart';
8 | import 'package:football/data/data.dart';
9 |
10 | class NewsRepositoryImp with NewsRepository {
11 | final NewsLocalDataSourceImpl newsLocalDataSourceImpl;
12 | final NewsRemoteDataSource newsRemoteRepository;
13 | final SecretLoader secretLoader;
14 |
15 | NewsRepositoryImp(this.newsLocalDataSourceImpl, this.newsRemoteRepository, this.secretLoader);
16 |
17 | @override
18 | Future>> fetchNews() async {
19 | Either> saveResult;
20 |
21 | final apiKey = await secretLoader.load();
22 |
23 | final apiResult = await newsRemoteRepository.sport(apiKey.newsApiKey);
24 |
25 | if (apiResult.isRight()) {
26 | final result = apiResult.getOrElse(() => null);
27 | if (result.articles != null) {
28 | await newsLocalDataSourceImpl.deleteAll();
29 | final listOfKeys = await newsLocalDataSourceImpl.saveArticles(
30 | result.articles.map((e) => ArticleLocal.fromArticle(e)).toList());
31 | saveResult = Right(listOfKeys);
32 | } else
33 | saveResult = left(Failure(message: "Empty news"));
34 | } else
35 | saveResult = apiResult.flatMap((a) => null);
36 |
37 | return saveResult;
38 | }
39 |
40 | @override
41 | Stream watchNews() =>
42 | newsLocalDataSourceImpl.watchArticle().map((event) => News.fromArticle(event.key, event.value));
43 | }
44 |
--------------------------------------------------------------------------------
/lib/model/match.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:football/data/model/games_local.dart';
3 | import 'package:football/domain/domain.dart';
4 |
5 | class Match {
6 | final int id;
7 | final String homeImageUrl;
8 | final String awayImageUrl;
9 | final String currentMinute;
10 | final bool isLive;
11 | final String league;
12 | final String homeTeam;
13 | final String awayTeam;
14 | final int homeScore;
15 | final int awayScore;
16 | final String playTime;
17 | final String playDate;
18 |
19 | Match(
20 | {@required this.id,
21 | this.homeImageUrl,
22 | this.awayImageUrl,
23 | this.currentMinute,
24 | this.isLive = false,
25 | this.league,
26 | this.homeTeam,
27 | this.awayTeam,
28 | this.homeScore,
29 | this.awayScore,
30 | this.playTime,
31 | this.playDate});
32 |
33 | static Match fromGamesLocal(GamesLocal gamesLocal) {
34 | return Match(
35 | id: gamesLocal.id,
36 | homeImageUrl: gamesLocal.homeTeamLogo,
37 | awayImageUrl: gamesLocal.awayTeamLogo,
38 | currentMinute: "15",
39 | isLive: gamesLocal.status == "LIVE" ||
40 | gamesLocal.status == "IN_PLAY" ||
41 | gamesLocal.status == "PAUSED",
42 | league: gamesLocal.leagueName,
43 | homeTeam: gamesLocal.homeTeamName,
44 | awayTeam: gamesLocal.awayTeamName,
45 | homeScore: gamesLocal.scoreFullTimeHomeTeam ??
46 | gamesLocal.scoreHalfTimeHomeTeam,
47 | awayScore: gamesLocal.scoreFullTimeAwayTeam ??
48 | gamesLocal.scoreHalfTimeAwayTeam,
49 | playTime: Utils.toAppTime(gamesLocal.utcDate),
50 | playDate: Utils.toAppDate(gamesLocal.utcDate) );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/screens/home_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:football/config/palette.dart';
3 | import 'package:football/screens/screen.dart';
4 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
5 | import 'package:theme_mode_handler/theme_mode_handler.dart';
6 |
7 | class HomeScreen extends StatefulWidget {
8 | @override
9 | _HomeScreenState createState() => _HomeScreenState();
10 | }
11 |
12 | class _HomeScreenState extends State {
13 | final _destinations = [
14 | FootballScreen(),
15 | NewsScreen(),
16 | BookmarkScreen(),
17 | ProfileScreen()
18 | ];
19 |
20 | final _bottomNavItem = {
21 | "Football": MdiIcons.soccer,
22 | "News": Icons.chat_bubble_outline,
23 | "Bookmark": Icons.done_outline,
24 | "Profile": Icons.person_outline
25 | };
26 |
27 | int _currentIndex = 0;
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 |
32 | Palette.setUpStatusBarThemeMode(ThemeModeHandler.of(context).themeMode);
33 |
34 | return Scaffold(
35 | body: IndexedStack(
36 | index: _currentIndex,
37 | children: _destinations,
38 | ),
39 | bottomNavigationBar: BottomNavigationBar(
40 | iconSize: 28.0,
41 | currentIndex: _currentIndex,
42 | onTap: (index) => setState(() => _currentIndex = index),
43 | type: BottomNavigationBarType.fixed,
44 | items: _bottomNavItem
45 | .map((title, icon) => MapEntry(
46 | title,
47 | BottomNavigationBarItem(
48 | icon: Icon(icon),
49 | title: Text(title)
50 | )))
51 | .values
52 | .toList(),
53 | ),
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | football
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | io.flutter.embedded_views_preview
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/lib/data/model/league_table_local.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:football/data/data.dart';
3 | import 'package:hive/hive.dart';
4 |
5 | part 'league_table_local.g.dart';
6 |
7 | @HiveType()
8 | class LeagueTableLocal {
9 | @HiveField(0)
10 | int id; //league id
11 | @HiveField(1)
12 | String leagueName;
13 | @HiveField(2)
14 | int position;
15 | @HiveField(3)
16 | int teamId;
17 | @HiveField(4)
18 | String teamName;
19 | @HiveField(5)
20 | String teamLogo;
21 | @HiveField(6)
22 | int playedGames;
23 | @HiveField(7)
24 | String form;
25 | @HiveField(8)
26 | int won;
27 | @HiveField(9)
28 | int draw;
29 | @HiveField(10)
30 | int lost;
31 | @HiveField(11)
32 | int points;
33 | @HiveField(12)
34 | int goalsFor;
35 | @HiveField(13)
36 | int goalsAgainst;
37 | @HiveField(14)
38 | int goalDifference;
39 |
40 | LeagueTableLocal(this.id, this.leagueName, this.position, this.teamId,
41 | this.teamName, this.teamLogo, this.playedGames, this.form, this.won,
42 | this.draw, this.lost, this.points, this.goalsFor, this.goalsAgainst,
43 | this.goalDifference);
44 |
45 | static List fromTable(TableResponse tableResponse) {
46 | final leagueId = tableResponse.competition.id;
47 | final leagueName = tableResponse.competition.name;
48 | final standing = tableResponse.standings[0];
49 | if(standing != null && standing.table.isNotEmpty){
50 | return standing.table.map((table) =>
51 | LeagueTableLocal(leagueId, leagueName, table.position, table.team.id,
52 | table.team.name, table.team.crestUrl, table.playedGames, table.form,
53 | table.won, table.draw, table.lost, table.points, table.goalsFor,
54 | table.goalsAgainst, table.goalDifference)
55 | ).toList();
56 |
57 | } else return null;
58 |
59 | }
60 | }
--------------------------------------------------------------------------------
/lib/widgets/sliver_custom_appbar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:football/config/palette.dart';
3 | import 'package:theme_mode_handler/theme_mode_handler.dart';
4 |
5 | class SliverCustomAppBar extends StatelessWidget {
6 |
7 | final String title;
8 | final String flexibleTitle;
9 | final Widget trailing;
10 | final IconData iconLeading;
11 | final Function onLeadingTap;
12 |
13 | const SliverCustomAppBar({Key key, this.title, this.flexibleTitle, this.trailing, this.iconLeading, this.onLeadingTap}) : super(key: key);
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | final themeMode = ThemeModeHandler.of(context).themeMode;
18 | return SliverAppBar(
19 | floating: true,
20 | pinned: title != null ? true : false,
21 | backgroundColor: Theme.of(context).scaffoldBackgroundColor,
22 | iconTheme: IconThemeData(),
23 | brightness: Palette.appBarTitleBrightness(themeMode),
24 | actions: trailing != null ? [trailing] : null,
25 | centerTitle: false,
26 | expandedHeight: flexibleTitle != null ? 100.0 : 0.0,
27 | title: title != null ? Text(title, style: TextStyle(fontWeight: FontWeight.bold, color: Palette.appBarTitleColor(themeMode)),) : SizedBox.shrink(),
28 | leading: iconLeading != null ? InkWell(child: Icon(iconLeading, size: 20.0), onTap: onLeadingTap): SizedBox.shrink(),
29 | flexibleSpace: flexibleTitle != null ? FlexibleSpaceBar(
30 | titlePadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
31 | centerTitle: false,
32 | title: Text(flexibleTitle, style: TextStyle(
33 | fontSize: 25.0,
34 | letterSpacing: -1,
35 | color: Palette.appBarTitleColor(themeMode),
36 | fontWeight: FontWeight.bold)),
37 | ): SizedBox.shrink(),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/widgets/custom_search_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CustomSearchBar extends StatelessWidget {
4 | final Function onTapSearch;
5 | final bool canStartSearch;
6 | final bool showCursor;
7 | final bool readOnly;
8 |
9 | const CustomSearchBar(
10 | {Key key,
11 | this.onTapSearch,
12 | this.canStartSearch = false,
13 | this.showCursor = false,
14 | this.readOnly = true})
15 | : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Row(
20 | children: [
21 | Expanded(
22 | child: ClipRRect(
23 | borderRadius: BorderRadius.circular(10.0),
24 | child: Container(
25 | height: 35.0,
26 | color: Theme.of(context).hoverColor,
27 | child: Row(
28 | children: [
29 | SizedBox(width: 10.0),
30 | Icon(Icons.search),
31 | SizedBox(width: 5.0),
32 | Expanded(
33 | child: TextFormField(
34 | onTap: readOnly ? onTapSearch : null,
35 | showCursor: showCursor,
36 | readOnly: readOnly,
37 | decoration: InputDecoration(
38 | hintText: "Search", border: InputBorder.none),
39 | ),
40 | ),
41 | Icon(Icons.mic_none),
42 | SizedBox(width: 8.0),
43 | ],
44 | ),
45 | ),
46 | ),
47 | ),
48 | canStartSearch
49 | ? FlatButton(
50 | child: Text(("Cancel")),
51 | splashColor: Colors.transparent,
52 | highlightColor: Colors.transparent,
53 | onPressed: () => Navigator.of(context).pop(),
54 | )
55 | : SizedBox.shrink()
56 | ],
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "com.efhem.football"
42 | // Required by the Flutter WebView plugin.
43 | minSdkVersion 19
44 | targetSdkVersion 28
45 | versionCode flutterVersionCode.toInteger()
46 | versionName flutterVersionName
47 | }
48 |
49 | buildTypes {
50 | release {
51 | // TODO: Add your own signing config for the release build.
52 | // Signing with the debug keys for now, so `flutter run --release` works.
53 | signingConfig signingConfigs.debug
54 | }
55 | }
56 | }
57 |
58 | flutter {
59 | source '../..'
60 | }
61 |
62 | dependencies {
63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64 | }
65 |
--------------------------------------------------------------------------------
/lib/data/model/league_table_local.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'league_table_local.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class LeagueTableLocalAdapter extends TypeAdapter {
10 | @override
11 | LeagueTableLocal read(BinaryReader reader) {
12 | var numOfFields = reader.readByte();
13 | var fields = {
14 | for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
15 | };
16 | return LeagueTableLocal(
17 | fields[0] as int,
18 | fields[1] as String,
19 | fields[2] as int,
20 | fields[3] as int,
21 | fields[4] as String,
22 | fields[5] as String,
23 | fields[6] as int,
24 | fields[7] as String,
25 | fields[8] as int,
26 | fields[9] as int,
27 | fields[10] as int,
28 | fields[11] as int,
29 | fields[12] as int,
30 | fields[13] as int,
31 | fields[14] as int,
32 | );
33 | }
34 |
35 | @override
36 | void write(BinaryWriter writer, LeagueTableLocal obj) {
37 | writer
38 | ..writeByte(15)
39 | ..writeByte(0)
40 | ..write(obj.id)
41 | ..writeByte(1)
42 | ..write(obj.leagueName)
43 | ..writeByte(2)
44 | ..write(obj.position)
45 | ..writeByte(3)
46 | ..write(obj.teamId)
47 | ..writeByte(4)
48 | ..write(obj.teamName)
49 | ..writeByte(5)
50 | ..write(obj.teamLogo)
51 | ..writeByte(6)
52 | ..write(obj.playedGames)
53 | ..writeByte(7)
54 | ..write(obj.form)
55 | ..writeByte(8)
56 | ..write(obj.won)
57 | ..writeByte(9)
58 | ..write(obj.draw)
59 | ..writeByte(10)
60 | ..write(obj.lost)
61 | ..writeByte(11)
62 | ..write(obj.points)
63 | ..writeByte(12)
64 | ..write(obj.goalsFor)
65 | ..writeByte(13)
66 | ..write(obj.goalsAgainst)
67 | ..writeByte(14)
68 | ..write(obj.goalDifference);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Football
2 |
3 | Football is an attempt to build a full featured flutter app that shows favourite league tables, teams, standings, live scores and generally sport news.
4 | The focus of this project is to use different widgets available on flutter and especially on demonstrating how to structure your code, design your architecture,
5 | and the eventual impact of adopting these patterns on testing and maintaining your app.
6 |
7 | ## Getting Started
8 |
9 | Clone the repository. The app make use of [football-data](https://www.football-data.org/) and [New](https://newsapi.org/) API. click the link to get your api key if you dont have.
10 | - Create a file at the root of your project call it `secrets.json`
11 | - Copy and paste below json into the file.
12 |
13 | ```
14 | {
15 | "news_api_key": "your-news-api-key",
16 | "football_api_key": "your-football_api_key"
17 | }
18 | ```
19 |
20 | ## UI
21 | UI inspired by [here](https://www.uplabs.com/posts/profile-light-mode-and-dark-mode) and [here](https://www.uplabs.com/posts/sport-news-app-564029c3-8787-4417-87b8-9047a58b29b4)
22 |
23 | 
24 |
25 | ## Libraries
26 |
27 | - [Retrofit For Dart](https://pub.dev/packages/retrofit) for easy CRUD request
28 | - [dio](https://github.com/flutterchina/dio/) A powerful Http client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc.
29 | - [get it](https://pub.dev/packages/get_it) Service Locator for Dart and Flutter projects
30 | - [dartz](https://pub.dev/packages/dartz) Functional programming in Dart
31 | - [hive](https://docs.hivedb.dev/#/README) Super fast database written in pure Dart
32 |
33 | ## Todo
34 |
35 | The app in this project aims to be simple enough that you can understand it quickly, but complex enough to showcase difficult design decisions and testing scenarios.
36 |
37 | There are some improvement i intend to make
38 | - Using BLOC for better state management
39 | - Building setting screen
40 | - Integrate firebase (chat, room or group) for user interaction on the app
41 | - Animations
42 | - Test
43 |
--------------------------------------------------------------------------------
/lib/viewmodel/football_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/domain/repository/football_repository.dart';
2 | import 'package:football/model/match.dart';
3 | import 'package:football/model/table_item.dart';
4 | import 'package:football/model/team.dart';
5 | import 'package:football/viewmodel/base_viewmodel.dart';
6 |
7 | class FootballProvider extends BaseViewModel {
8 | final FootballRepository footballRepository;
9 |
10 | FootballProvider(this.footballRepository);
11 |
12 | List _liveMatch = List();
13 | List get liveMatch => _liveMatch;
14 |
15 | List _nonLiveMatch = List();
16 | List get nonLiveMatch => _nonLiveMatch;
17 |
18 | List teams(int leagueId) {
19 | return footballRepository.getLeagueTeam(leagueId);
20 | }
21 |
22 | List table(int leagueId) {
23 | final result = footballRepository.getLeagueTable(leagueId);
24 | result.sort((a, b) => a.position.compareTo(b.position));
25 | return result;
26 | }
27 |
28 | List topTeams() {
29 | return footballRepository.topTeams();
30 | }
31 |
32 | List fiveTopTeams() {
33 | final teams = topTeams();
34 | teams.sort((a, b) => a.position.compareTo(b.position));
35 | return teams.length > 5
36 | ? teams.sublist(0, 5)
37 | : teams.sublist(0, teams.length);
38 | }
39 |
40 | void fetchTable(int leagueId) async {
41 | await Future.delayed(Duration(milliseconds: 500));
42 | setViewState(ViewState.loading);
43 |
44 | final result = await footballRepository.fetchTable(leagueId);
45 | result.fold((l) => setError(l), (r) => setError(null));
46 | setViewState(ViewState.loaded);
47 | }
48 |
49 | void fetchGames(int leagueId) async {
50 | await Future.delayed(Duration(milliseconds: 500));
51 | setViewState(ViewState.loading);
52 |
53 | final result = await footballRepository.fetchGames(leagueId);
54 | result.fold((l) => setError(l), (r) {
55 | _liveMatch = r.where((e) => e.isLive).toList();
56 | _nonLiveMatch = r.where((e) => e.isLive == false).toList();
57 | notifyListeners();
58 | });
59 | setViewState(ViewState.loaded);
60 | }
61 | }
--------------------------------------------------------------------------------
/lib/injection_container.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/datasource/datasource.dart';
2 | import 'package:football/data/datasource/footbal/football_local_data_source.dart';
3 | import 'package:football/data/datasource/footbal/football_remote_data_source_imp.dart';
4 | import 'package:football/data/repository/football_repository_imp.dart';
5 | import 'package:football/data/retrofit/api_service.dart';
6 | import 'package:football/viewmodel/provider.dart';
7 | import 'package:get_it/get_it.dart';
8 | import 'package:football/domain/domain.dart';
9 | import 'package:football/data/data.dart';
10 |
11 |
12 | import 'data/repository/news_repository_imp.dart';
13 | import 'data/datasource/footbal/news_remote_datasource.dart';
14 |
15 | final injector = GetIt.instance;
16 |
17 | Future init() async {
18 |
19 | //provide secret keys
20 | injector.registerSingleton(SecretLoader(secretPath: "secrets.json") );
21 |
22 | //provide ApiService
23 | final SecretLoader secretLoader = injector.get();
24 | final apikey = await secretLoader.load();
25 | injector.registerLazySingleton(() => ApiService.create(apikey.footballApiKey));
26 |
27 | //NewsRemoteDataSourceImpl
28 | injector.registerLazySingleton(() => NewsRemoteDataSourceImpl(apiService: injector.get()));
29 | //NewsRemoteDataSourceImpl
30 | injector.registerLazySingleton(() => NewsLocalDataSourceImpl());
31 | //NewsRepository
32 | injector.registerLazySingleton(() => NewsRepositoryImp(injector.get(), injector.get(), injector.get()));
33 |
34 | //FootballRemoteDataSourceImpl
35 | injector.registerLazySingleton(() => FootballRemoteDataSourceImpl(apiService: injector.get()));
36 | //FootballRemoteDataSourceImpl
37 | injector.registerLazySingleton(() => FootballLocalDataSourceImpl());
38 | //FootballRepository
39 | injector.registerLazySingleton(() => FootballRepositoryImp(injector.get(), injector.get()));
40 |
41 |
42 | injector.registerFactory(() => NewsProvider(newsRepository: injector.get()));
43 | injector.registerFactory(() => FootballProvider(injector.get()));
44 | }
45 |
--------------------------------------------------------------------------------
/lib/data/model/games_local.dart:
--------------------------------------------------------------------------------
1 | import 'package:football/data/data.dart';
2 | import 'package:football/data/model/games_response.dart';
3 | import 'package:hive/hive.dart';
4 |
5 | part 'games_local.g.dart';
6 |
7 | @HiveType()
8 | class GamesLocal {
9 | @HiveField(0)
10 | int leagueId;
11 | @HiveField(1)
12 | String leagueName;
13 | @HiveField(2)
14 | int id; //match id
15 | @HiveField(3)
16 | String utcDate;
17 | @HiveField(4)
18 | String status;
19 | @HiveField(5)
20 | int matchDay;
21 | @HiveField(6)
22 | String winner;
23 | @HiveField(7)
24 | String duration;
25 | @HiveField(8)
26 | int scoreFullTimeHomeTeam;
27 | @HiveField(9)
28 | int scoreFullTimeAwayTeam;
29 | @HiveField(10)
30 | int scoreHalfTimeHomeTeam;
31 | @HiveField(11)
32 | int scoreHalfTimeAwayTeam;
33 | @HiveField(12)
34 | int homeTeamId;
35 | @HiveField(13)
36 | String homeTeamName;
37 | @HiveField(14)
38 | String homeTeamLogo;
39 | @HiveField(15)
40 | int awayTeamId;
41 | @HiveField(16)
42 | String awayTeamName;
43 | @HiveField(17)
44 | String awayTeamLogo;
45 | @HiveField(18)
46 | String lastUpdated;
47 |
48 | GamesLocal(
49 | this.leagueId,
50 | this.leagueName,
51 | this.id,
52 | this.utcDate,
53 | this.status,
54 | this.matchDay,
55 | this.winner,
56 | this.duration,
57 | this.scoreFullTimeHomeTeam,
58 | this.scoreFullTimeAwayTeam,
59 | this.scoreHalfTimeHomeTeam,
60 | this.scoreHalfTimeAwayTeam,
61 | this.homeTeamId,
62 | this.homeTeamName,
63 | this.homeTeamLogo,
64 | this.awayTeamId,
65 | this.awayTeamName,
66 | this.awayTeamLogo,
67 | this.lastUpdated);
68 |
69 | static GamesLocal fromMatches(Competition competition, Matches match,
70 | String homeTeamLogo, String awayTeamLogo){
71 |
72 | return GamesLocal(
73 | competition.id,
74 | competition.name,
75 | match.id, match.utcDate, match.status, match.matchday,
76 | match.score.winner,
77 | match.score.duration,
78 | match.score.fullTime.homeTeam, //scoreFullTimeHomeTeam
79 | match.score.fullTime.awayTeam,
80 | match.score.halfTime.homeTeam,
81 | match.score.halfTime.awayTeam,
82 | match.homeTeam.id, //homeTeamId,
83 | match.homeTeam.name,
84 | homeTeamLogo,
85 | match.awayTeam.id,
86 | match.awayTeam.name,
87 | awayTeamLogo,
88 | match.lastUpdated
89 | );
90 | }
91 | }
--------------------------------------------------------------------------------
/lib/data/model/games_local.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'games_local.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class GamesLocalAdapter extends TypeAdapter {
10 | @override
11 | GamesLocal read(BinaryReader reader) {
12 | var numOfFields = reader.readByte();
13 | var fields = {
14 | for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
15 | };
16 | return GamesLocal(
17 | fields[0] as int,
18 | fields[1] as String,
19 | fields[2] as int,
20 | fields[3] as String,
21 | fields[4] as String,
22 | fields[5] as int,
23 | fields[6] as String,
24 | fields[7] as String,
25 | fields[8] as int,
26 | fields[9] as int,
27 | fields[10] as int,
28 | fields[11] as int,
29 | fields[12] as int,
30 | fields[13] as String,
31 | fields[14] as String,
32 | fields[15] as int,
33 | fields[16] as String,
34 | fields[17] as String,
35 | fields[18] as String,
36 | );
37 | }
38 |
39 | @override
40 | void write(BinaryWriter writer, GamesLocal obj) {
41 | writer
42 | ..writeByte(19)
43 | ..writeByte(0)
44 | ..write(obj.leagueId)
45 | ..writeByte(1)
46 | ..write(obj.leagueName)
47 | ..writeByte(2)
48 | ..write(obj.id)
49 | ..writeByte(3)
50 | ..write(obj.utcDate)
51 | ..writeByte(4)
52 | ..write(obj.status)
53 | ..writeByte(5)
54 | ..write(obj.matchDay)
55 | ..writeByte(6)
56 | ..write(obj.winner)
57 | ..writeByte(7)
58 | ..write(obj.duration)
59 | ..writeByte(8)
60 | ..write(obj.scoreFullTimeHomeTeam)
61 | ..writeByte(9)
62 | ..write(obj.scoreFullTimeAwayTeam)
63 | ..writeByte(10)
64 | ..write(obj.scoreHalfTimeHomeTeam)
65 | ..writeByte(11)
66 | ..write(obj.scoreHalfTimeAwayTeam)
67 | ..writeByte(12)
68 | ..write(obj.homeTeamId)
69 | ..writeByte(13)
70 | ..write(obj.homeTeamName)
71 | ..writeByte(14)
72 | ..write(obj.homeTeamLogo)
73 | ..writeByte(15)
74 | ..write(obj.awayTeamId)
75 | ..writeByte(16)
76 | ..write(obj.awayTeamName)
77 | ..writeByte(17)
78 | ..write(obj.awayTeamLogo)
79 | ..writeByte(18)
80 | ..write(obj.lastUpdated);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
12 |
19 |
23 |
27 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
43 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/data/retrofit/api_service.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'api_service.dart';
4 |
5 | // **************************************************************************
6 | // RetrofitGenerator
7 | // **************************************************************************
8 |
9 | class _ApiService implements ApiService {
10 | _ApiService(this._dio, {this.baseUrl}) {
11 | ArgumentError.checkNotNull(_dio, '_dio');
12 | baseUrl ??= 'https://api.football-data.org/v2/';
13 | }
14 |
15 | final Dio _dio;
16 |
17 | String baseUrl;
18 |
19 | @override
20 | Future news(
21 | {category = "sport", country = "us", apiKey}) async {
22 | const _extra = {};
23 | final queryParameters = {
24 | r'category': category,
25 | r'country': country,
26 | r'apiKey': apiKey
27 | };
28 | queryParameters.removeWhere((k, v) => v == null);
29 | final _data = {};
30 | final _result = await _dio.request