├── lib ├── common │ └── enums.dart ├── services │ ├── sharing_service.dart │ ├── story_service.dart │ └── preferences_service.dart ├── screens │ ├── top_stories_page.dart │ ├── new_stories_page.dart │ ├── favourites_page.dart │ ├── settings_page.dart │ └── stories_page.dart ├── widgets │ ├── styles.dart │ ├── placeholder_stories.dart │ ├── placeholder_container.dart │ ├── app.dart │ ├── placeholder_story.dart │ ├── themeable_app.dart │ └── story.dart ├── stores │ ├── new_stories_store.dart │ ├── top_stories_store.dart │ ├── settings_store.dart │ ├── favourites_store.dart │ ├── settings_store.g.dart │ ├── favourites_store.g.dart │ ├── stories_store.dart │ └── stories_store.g.dart └── main.dart ├── fonts ├── Lato-Italic.ttf ├── Lato-Light.ttf ├── Lato-Medium.ttf └── Lato-Regular.ttf ├── android ├── app │ ├── src │ │ ├── main │ │ │ ├── ic_launcher-web.png │ │ │ ├── res │ │ │ │ ├── drawable │ │ │ │ │ ├── launcher_image.png │ │ │ │ │ ├── launch_background.xml │ │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ └── values │ │ │ │ │ └── styles.xml │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── crossingthestreams │ │ │ │ │ └── supnews │ │ │ │ │ └── MainActivity.java │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── proguard-rules.pro │ └── build.gradle ├── gradle.properties ├── .gitignore ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ ├── flutter_export_environment.sh │ ├── Flutter.podspec │ └── AppFrameworkInfo.plist ├── Runner │ ├── AppDelegate.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── ItunesArtwork@2x.png │ │ │ ├── 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-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── main.m │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── .gitignore ├── .metadata ├── .gitignore ├── pubspec.yaml ├── LICENSE ├── README.md └── pubspec.lock /lib/common/enums.dart: -------------------------------------------------------------------------------- 1 | enum StoryFeedType { 2 | New, 3 | Top, 4 | } 5 | -------------------------------------------------------------------------------- /fonts/Lato-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/fonts/Lato-Italic.ttf -------------------------------------------------------------------------------- /fonts/Lato-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/fonts/Lato-Light.ttf -------------------------------------------------------------------------------- /fonts/Lato-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/fonts/Lato-Medium.ttf -------------------------------------------------------------------------------- /fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /android/app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /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 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | android.enableR8=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 -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launcher_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/drawable/launcher_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/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/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/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/MaikuB/supnews/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaikuB/supnews/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /lib/services/sharing_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:share/share.dart'; 2 | 3 | class SharingService { 4 | Future share(String url) { 5 | return Share.share(url); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #Flutter Wrapper 2 | -keep class io.flutter.app.** { *; } 3 | -keep class io.flutter.plugin.** { *; } 4 | -keep class io.flutter.util.** { *; } 5 | -keep class io.flutter.view.** { *; } 6 | -keep class io.flutter.** { *; } 7 | -keep class io.flutter.plugins.** { *; } -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jul 22 19:00:36 AEST 2020 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-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /lib/screens/top_stories_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'stories_page.dart'; 3 | import '../stores/top_stories_store.dart'; 4 | 5 | class TopStoriesPage extends StoriesPage { 6 | TopStoriesPage(TopStoriesStore store, {Key key}) : super(store, key: key); 7 | } 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/screens/new_stories_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import '../screens/stories_page.dart'; 3 | import '../stores/new_stories_store.dart'; 4 | 5 | class NewStoriesPage extends StoriesPage { 6 | NewStoriesPage(NewStoriesStore store, {Key key}) : super(store, key: key); 7 | } 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.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: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /lib/widgets/styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TextSpacer extends StatelessWidget { 4 | final Widget _child; 5 | 6 | const TextSpacer(this._child, {Key key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) => 10 | Padding(padding: const EdgeInsets.fromLTRB(0, 0, 0, 4), child: _child); 11 | } 12 | -------------------------------------------------------------------------------- /lib/widgets/placeholder_stories.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'placeholder_story.dart'; 3 | 4 | class PlaceholderStories extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return ListView.builder( 8 | itemCount: 1, 9 | itemBuilder: (context, index) => PlaceholderStory(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /lib/stores/new_stories_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:hnpwa_client/hnpwa_client.dart'; 2 | import '../common/enums.dart'; 3 | import '../services/preferences_service.dart'; 4 | import 'stories_store.dart'; 5 | 6 | class NewStoriesStore extends StoriesStore { 7 | NewStoriesStore( 8 | HnpwaClient hnpwaClient, PreferencesService preferencesService) 9 | : super(StoryFeedType.New, hnpwaClient, preferencesService); 10 | } 11 | -------------------------------------------------------------------------------- /lib/stores/top_stories_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:hnpwa_client/hnpwa_client.dart'; 2 | import '../common/enums.dart'; 3 | import '../services/preferences_service.dart'; 4 | import 'stories_store.dart'; 5 | 6 | class TopStoriesStore extends StoriesStore { 7 | TopStoriesStore( 8 | HnpwaClient hnpwaClient, PreferencesService preferencesService) 9 | : super(StoryFeedType.Top, hnpwaClient, preferencesService); 10 | } 11 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/crossingthestreams/supnews/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.crossingthestreams.supnews; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.5.3' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /lib/services/story_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:url_launcher/url_launcher.dart'; 2 | import '../services/preferences_service.dart'; 3 | 4 | class StoryService { 5 | final PreferencesService _preferencesService; 6 | 7 | StoryService(this._preferencesService); 8 | 9 | Future openInBrowser(String url) { 10 | return launch(url, forceSafariVC: false, forceWebView: false); 11 | } 12 | 13 | Future open(String url) async { 14 | final defaultOpenInAppPreference = _preferencesService.openInApp; 15 | return launch(url, 16 | forceSafariVC: defaultOpenInAppPreference, 17 | forceWebView: defaultOpenInAppPreference); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /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 | 34 | Podfile 35 | Podfile.lock -------------------------------------------------------------------------------- /ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/michaelbui/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/michaelbui/Projects/GitHub/supnews" 5 | export "FLUTTER_TARGET=/Users/michaelbui/Projects/GitHub/supnews/lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "OTHER_LDFLAGS=$(inherited) -framework Flutter" 9 | export "FLUTTER_FRAMEWORK_DIR=/Users/michaelbui/flutter/bin/cache/artifacts/engine/ios" 10 | export "FLUTTER_BUILD_NAME=1.0.2" 11 | export "FLUTTER_BUILD_NUMBER=1" 12 | export "TRACK_WIDGET_CREATION=true" 13 | export "DART_DEFINES=flutter.inspector.structuredErrors=true" 14 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart'; 4 | import 'services/preferences_service.dart'; 5 | import 'widgets/app.dart'; 6 | 7 | Future main() async { 8 | WidgetsFlutterBinding.ensureInitialized(); 9 | var sharedPreferences = await SharedPreferences.getInstance(); 10 | await FlutterStatusbarcolor.setStatusBarColor(Colors.teal); 11 | if (useWhiteForeground(Colors.teal)) { 12 | await FlutterStatusbarcolor.setStatusBarWhiteForeground(true); 13 | } else { 14 | await FlutterStatusbarcolor.setStatusBarWhiteForeground(false); 15 | } 16 | runApp(App(PreferencesService(sharedPreferences))); 17 | } 18 | -------------------------------------------------------------------------------- /ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: This podspec is NOT to be published. It is only used as a local source! 3 | # 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'Flutter' 7 | s.version = '1.0.0' 8 | s.summary = 'High-performance, high-fidelity mobile apps.' 9 | s.description = <<-DESC 10 | Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. 11 | DESC 12 | s.homepage = 'https://flutter.io' 13 | s.license = { :type => 'MIT' } 14 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 15 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 16 | s.ios.deployment_target = '8.0' 17 | s.vendored_frameworks = 'Flutter.framework' 18 | end 19 | -------------------------------------------------------------------------------- /.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 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Exceptions to above rules. 43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 44 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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/widgets/placeholder_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PlaceholderContainer extends AnimatedWidget { 4 | final Widget child; 5 | 6 | const PlaceholderContainer( 7 | {Key key, @required Animation animation, @required this.child}) 8 | : super(key: key, listenable: animation); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final Animation animation = listenable; 13 | return Container( 14 | decoration: BoxDecoration( 15 | gradient: LinearGradient( 16 | stops: animation.value < 0.9 17 | ? [animation.value, animation.value + 0.1, animation.value + 0.2] 18 | : [0.0, animation.value], 19 | colors: animation.value < 0.9 20 | ? [Colors.grey, Colors.grey.shade200, Colors.grey] 21 | : [Colors.grey, Colors.grey.shade200], 22 | ), 23 | ), 24 | child: child, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: supnews 2 | description: A new Flutter project. 3 | version: 1.0.2+1 4 | 5 | environment: 6 | sdk: ">=2.2.2 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | http: 12 | mobx: ^1.2.1+1 13 | flutter_mobx: ^1.1.0+1 14 | provider: ^3.0.0 15 | url_launcher: ^5.0.3 16 | cupertino_icons: ^0.1.2 17 | shared_preferences: ^0.5.3+1 18 | share: ^0.6.1+1 19 | intl: ^0.15.8 20 | hnpwa_client: 21 | flutter_statusbarcolor: ^0.2.0 22 | incrementally_loading_listview: ^0.1.0+1 23 | async: 24 | 25 | 26 | 27 | dev_dependencies: 28 | flutter_test: 29 | sdk: flutter 30 | mobx_codegen: ^1.1.0+1 31 | build_runner: ^1.10.0 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | fonts: 37 | - family: Lato 38 | fonts: 39 | - asset: fonts/Lato-Regular.ttf 40 | - asset: fonts/Lato-Italic.ttf 41 | style: italic 42 | - asset: fonts/Lato-Light.ttf 43 | weight: 300 44 | - asset: fonts/Lato-Medium.ttf 45 | weight: 500 46 | -------------------------------------------------------------------------------- /lib/services/preferences_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class PreferencesService { 4 | final String _useDarkModeKey = 'useDarkMode'; 5 | final String _openInAppKey = 'openInApp'; 6 | final String _favouritesKey = 'favourites'; 7 | 8 | final SharedPreferences _sharedPreferences; 9 | 10 | const PreferencesService(this._sharedPreferences); 11 | 12 | set useDarkMode(bool useDarkMode) { 13 | _sharedPreferences.setBool(_useDarkModeKey, useDarkMode); 14 | } 15 | 16 | bool get useDarkMode => _sharedPreferences.getBool(_useDarkModeKey) ?? false; 17 | 18 | bool get openInApp => _sharedPreferences.getBool(_openInAppKey) ?? true; 19 | 20 | List get favourites => 21 | _sharedPreferences.getStringList(_favouritesKey) ?? List(); 22 | 23 | set openInApp(bool openInApp) { 24 | _sharedPreferences.setBool(_openInAppKey, openInApp); 25 | } 26 | 27 | set favourites(List favourites) { 28 | _sharedPreferences.setStringList(_favouritesKey, favourites); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/stores/settings_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:mobx/mobx.dart'; 2 | import 'package:url_launcher/url_launcher.dart'; 3 | import '../services/preferences_service.dart'; 4 | part 'settings_store.g.dart'; 5 | 6 | class SettingsStore = SettingsStoreBase with _$SettingsStore; 7 | 8 | abstract class SettingsStoreBase with Store { 9 | PreferencesService _preferencesService; 10 | 11 | @observable 12 | bool useDarkMode; 13 | 14 | @observable 15 | bool openInApp; 16 | 17 | /// when the store is created, we read in the current settings immediately to avoid the scenario where 18 | /// the values displayed will change upon switching to the settings tab 19 | SettingsStoreBase(this._preferencesService) { 20 | useDarkMode = _preferencesService.useDarkMode; 21 | openInApp = _preferencesService.openInApp; 22 | } 23 | 24 | @action 25 | void setDarkMode(bool updatedDarkModePreference) { 26 | _preferencesService.useDarkMode = updatedDarkModePreference; 27 | useDarkMode = updatedDarkModePreference; 28 | } 29 | 30 | @action 31 | void setOpenInApp(bool updatedOpenInAppPreference) { 32 | _preferencesService.openInApp = updatedOpenInAppPreference; 33 | openInApp = updatedOpenInAppPreference; 34 | } 35 | 36 | Future showPrivacyPolicy() async { 37 | await launch('https://crossingthestreams.io/supnews-privacy-policy/', 38 | forceSafariVC: true, forceWebView: true); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/widgets/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hnpwa_client/hnpwa_client.dart'; 3 | import 'package:provider/provider.dart'; 4 | import '../services/sharing_service.dart'; 5 | import '../services/story_service.dart'; 6 | import '../stores/favourites_store.dart'; 7 | import '../stores/settings_store.dart'; 8 | import '../services/preferences_service.dart'; 9 | import 'themeable_app.dart'; 10 | 11 | class App extends StatelessWidget { 12 | final PreferencesService _preferencesService; 13 | 14 | const App(this._preferencesService); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return MultiProvider( 19 | providers: [ 20 | Provider( 21 | create: (_) => _preferencesService, 22 | ), 23 | Provider( 24 | create: (_) => HnpwaClient(), 25 | ), 26 | Provider( 27 | create: (_) => SettingsStore(_preferencesService), 28 | ), 29 | Provider( 30 | create: (_) => FavouritesStore(_preferencesService), 31 | ), 32 | Provider( 33 | create: (_) => SharingService(), 34 | ), 35 | Provider( 36 | create: (_) => StoryService(_preferencesService), 37 | ) 38 | ], 39 | child: Consumer( 40 | builder: (context, value, _) => ThemeableApp(value), 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/screens/favourites_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_mobx/flutter_mobx.dart'; 3 | import '../stores/favourites_store.dart'; 4 | import '../widgets/story.dart'; 5 | 6 | class FavouritesPage extends StatefulWidget { 7 | final FavouritesStore store; 8 | 9 | const FavouritesPage( 10 | this.store, { 11 | Key key, 12 | }) : super(key: key); 13 | 14 | @override 15 | _FavouritesPageState createState() => _FavouritesPageState(); 16 | } 17 | 18 | class _FavouritesPageState extends State 19 | with AutomaticKeepAliveClientMixin { 20 | @override 21 | void initState() { 22 | super.initState(); 23 | widget.store.loadFavourites(); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | super.build(context); 29 | return Observer( 30 | builder: (_) { 31 | if (widget.store.hasFavourites) { 32 | return ListView.builder( 33 | itemCount: widget.store.favourites.length, 34 | itemBuilder: (context, index) { 35 | return Story(widget.store.favourites[index]); 36 | }, 37 | ); 38 | } else { 39 | return Center( 40 | child: Column( 41 | mainAxisAlignment: MainAxisAlignment.center, 42 | children: [Icon(Icons.favorite), Text('No favourites here')]), 43 | ); 44 | } 45 | }, 46 | ); 47 | } 48 | 49 | @override 50 | bool get wantKeepAlive => true; 51 | } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Michael Bui 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /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/stores/favourites_store.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:hnpwa_client/hnpwa_client.dart'; 4 | import 'package:mobx/mobx.dart'; 5 | import '../services/preferences_service.dart'; 6 | 7 | part 'favourites_store.g.dart'; 8 | 9 | class FavouritesStore = FavouritesStoreBase with _$FavouritesStore; 10 | 11 | abstract class FavouritesStoreBase with Store { 12 | PreferencesService _preferencesService; 13 | 14 | FavouritesStoreBase(this._preferencesService); 15 | 16 | @observable 17 | ObservableList favourites = ObservableList(); 18 | 19 | @computed 20 | bool get hasFavourites => favourites.isNotEmpty; 21 | 22 | bool isInFavourites(FeedItem feedItem) => 23 | favourites.any((f) => f.id == feedItem.id); 24 | 25 | @action 26 | void loadFavourites() { 27 | _preferencesService.favourites 28 | .map((f) => FeedItem.fromJson(jsonDecode(f))) 29 | .forEach((f) => favourites.add(f)); 30 | } 31 | 32 | @action 33 | addFavourite(FeedItem item) { 34 | favourites.add(item); 35 | _saveFavourites(); 36 | } 37 | 38 | @action 39 | void removeFavourite(FeedItem item) { 40 | favourites.removeWhere((fi) => fi.id == item.id); 41 | _saveFavourites(); 42 | } 43 | 44 | void _saveFavourites() { 45 | _preferencesService.favourites = favourites.map((fi) { 46 | return jsonEncode({ 47 | 'id': fi.id, 48 | 'title': fi.title, 49 | 'points': fi.points, 50 | 'user': fi.user, 51 | 'time': fi.time, 52 | 'time_ago': fi.timeAgo, 53 | 'comments_count': fi.commentsCount, 54 | 'url': fi.url, 55 | 'domain': fi.domain, 56 | }); 57 | }).toList(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | SUpNews 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | supnews 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /lib/screens/settings_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_mobx/flutter_mobx.dart'; 3 | import '../stores/settings_store.dart'; 4 | 5 | class SettingsPage extends StatefulWidget { 6 | final SettingsStore store; 7 | 8 | SettingsPage(this.store, {Key key}) : super(key: key); 9 | @override 10 | _SettingsPageState createState() => _SettingsPageState(); 11 | } 12 | 13 | class _SettingsPageState extends State 14 | with AutomaticKeepAliveClientMixin { 15 | @override 16 | Widget build(BuildContext context) { 17 | super.build(context); 18 | return Column( 19 | children: [ 20 | Observer( 21 | builder: (_) => Padding( 22 | padding: EdgeInsets.fromLTRB(0, 0, 0, 16), 23 | child: SwitchListTile( 24 | title: Text('Dark mode'), 25 | subtitle: Text( 26 | 'Note: this won\'t affect the articles that are displayed'), 27 | value: widget.store.useDarkMode, 28 | onChanged: (bool value) { 29 | widget.store.setDarkMode(value); 30 | }, 31 | ), 32 | ), 33 | ), 34 | Observer( 35 | builder: (_) => SwitchListTile( 36 | title: Text('Display stories within app'), 37 | subtitle: Text( 38 | 'Controls the default behaviour when tapping on a story to read'), 39 | value: widget.store.openInApp, 40 | onChanged: (bool value) { 41 | widget.store.setOpenInApp(value); 42 | }, 43 | ), 44 | ), 45 | ListTile( 46 | title: Text('Privacy policy'), 47 | onTap: () async { 48 | await widget.store.showPrivacyPolicy(); 49 | }, 50 | ), 51 | ], 52 | ); 53 | } 54 | 55 | @override 56 | bool get wantKeepAlive => true; 57 | } 58 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 11 | 17 | 24 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/stores/settings_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'settings_store.dart'; 4 | 5 | // ************************************************************************** 6 | // StoreGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 10 | 11 | mixin _$SettingsStore on SettingsStoreBase, Store { 12 | final _$useDarkModeAtom = Atom(name: 'SettingsStoreBase.useDarkMode'); 13 | 14 | @override 15 | bool get useDarkMode { 16 | _$useDarkModeAtom.reportRead(); 17 | return super.useDarkMode; 18 | } 19 | 20 | @override 21 | set useDarkMode(bool value) { 22 | _$useDarkModeAtom.reportWrite(value, super.useDarkMode, () { 23 | super.useDarkMode = value; 24 | }); 25 | } 26 | 27 | final _$openInAppAtom = Atom(name: 'SettingsStoreBase.openInApp'); 28 | 29 | @override 30 | bool get openInApp { 31 | _$openInAppAtom.reportRead(); 32 | return super.openInApp; 33 | } 34 | 35 | @override 36 | set openInApp(bool value) { 37 | _$openInAppAtom.reportWrite(value, super.openInApp, () { 38 | super.openInApp = value; 39 | }); 40 | } 41 | 42 | final _$SettingsStoreBaseActionController = 43 | ActionController(name: 'SettingsStoreBase'); 44 | 45 | @override 46 | void setDarkMode(bool updatedDarkModePreference) { 47 | final _$actionInfo = _$SettingsStoreBaseActionController.startAction( 48 | name: 'SettingsStoreBase.setDarkMode'); 49 | try { 50 | return super.setDarkMode(updatedDarkModePreference); 51 | } finally { 52 | _$SettingsStoreBaseActionController.endAction(_$actionInfo); 53 | } 54 | } 55 | 56 | @override 57 | void setOpenInApp(bool updatedOpenInAppPreference) { 58 | final _$actionInfo = _$SettingsStoreBaseActionController.startAction( 59 | name: 'SettingsStoreBase.setOpenInApp'); 60 | try { 61 | return super.setOpenInApp(updatedOpenInAppPreference); 62 | } finally { 63 | _$SettingsStoreBaseActionController.endAction(_$actionInfo); 64 | } 65 | } 66 | 67 | @override 68 | String toString() { 69 | return ''' 70 | useDarkMode: ${useDarkMode}, 71 | openInApp: ${openInApp} 72 | '''; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/stores/favourites_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'favourites_store.dart'; 4 | 5 | // ************************************************************************** 6 | // StoreGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 10 | 11 | mixin _$FavouritesStore on FavouritesStoreBase, Store { 12 | Computed _$hasFavouritesComputed; 13 | 14 | @override 15 | bool get hasFavourites => 16 | (_$hasFavouritesComputed ??= Computed(() => super.hasFavourites, 17 | name: 'FavouritesStoreBase.hasFavourites')) 18 | .value; 19 | 20 | final _$favouritesAtom = Atom(name: 'FavouritesStoreBase.favourites'); 21 | 22 | @override 23 | ObservableList get favourites { 24 | _$favouritesAtom.reportRead(); 25 | return super.favourites; 26 | } 27 | 28 | @override 29 | set favourites(ObservableList value) { 30 | _$favouritesAtom.reportWrite(value, super.favourites, () { 31 | super.favourites = value; 32 | }); 33 | } 34 | 35 | final _$FavouritesStoreBaseActionController = 36 | ActionController(name: 'FavouritesStoreBase'); 37 | 38 | @override 39 | void loadFavourites() { 40 | final _$actionInfo = _$FavouritesStoreBaseActionController.startAction( 41 | name: 'FavouritesStoreBase.loadFavourites'); 42 | try { 43 | return super.loadFavourites(); 44 | } finally { 45 | _$FavouritesStoreBaseActionController.endAction(_$actionInfo); 46 | } 47 | } 48 | 49 | @override 50 | dynamic addFavourite(FeedItem item) { 51 | final _$actionInfo = _$FavouritesStoreBaseActionController.startAction( 52 | name: 'FavouritesStoreBase.addFavourite'); 53 | try { 54 | return super.addFavourite(item); 55 | } finally { 56 | _$FavouritesStoreBaseActionController.endAction(_$actionInfo); 57 | } 58 | } 59 | 60 | @override 61 | void removeFavourite(FeedItem item) { 62 | final _$actionInfo = _$FavouritesStoreBaseActionController.startAction( 63 | name: 'FavouritesStoreBase.removeFavourite'); 64 | try { 65 | return super.removeFavourite(item); 66 | } finally { 67 | _$FavouritesStoreBaseActionController.endAction(_$actionInfo); 68 | } 69 | } 70 | 71 | @override 72 | String toString() { 73 | return ''' 74 | favourites: ${favourites}, 75 | hasFavourites: ${hasFavourites} 76 | '''; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/stores/stories_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:hnpwa_client/hnpwa_client.dart'; 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:url_launcher/url_launcher.dart'; 4 | import '../common/enums.dart'; 5 | import '../services/preferences_service.dart'; 6 | 7 | part 'stories_store.g.dart'; 8 | 9 | class StoriesStore = StoriesStoreBase with _$StoriesStore; 10 | 11 | abstract class StoriesStoreBase with Store { 12 | final StoryFeedType _storyFeedType; 13 | final HnpwaClient _hnpwaClient; 14 | final PreferencesService _preferencesService; 15 | 16 | int _currentPage = 1; 17 | bool _isLoadingNextPage = false; 18 | 19 | @observable 20 | bool hasNextPage = false; 21 | 22 | @observable 23 | ObservableList feedItems = ObservableList(); 24 | 25 | @observable 26 | ObservableFuture loadFeedItemsFuture; 27 | 28 | @observable 29 | bool loadingNextPage = false; 30 | 31 | StoriesStoreBase( 32 | this._storyFeedType, this._hnpwaClient, this._preferencesService); 33 | 34 | @action 35 | Future refresh() { 36 | return _loadFirstPageStories(); 37 | } 38 | 39 | @action 40 | Future retry() { 41 | return loadFeedItemsFuture = ObservableFuture(_loadFirstPageStories()); 42 | } 43 | 44 | @action 45 | Future loadInitialStories() { 46 | return loadFeedItemsFuture = ObservableFuture(_loadFirstPageStories()); 47 | } 48 | 49 | Future open(String url) { 50 | final defaultOpenInAppPreference = _preferencesService.openInApp; 51 | return launch(url, 52 | forceSafariVC: defaultOpenInAppPreference, 53 | forceWebView: defaultOpenInAppPreference); 54 | } 55 | 56 | @action 57 | Future loadNextPage() async { 58 | try { 59 | if (_isLoadingNextPage || (_currentPage > 1 && !hasNextPage)) { 60 | return; 61 | } 62 | _isLoadingNextPage = true; 63 | var feed = _storyFeedType == StoryFeedType.Top 64 | ? (await _hnpwaClient.news(page: _currentPage)) 65 | : (await _hnpwaClient.newest(page: _currentPage)); 66 | // some items from the official API don't have a URL but the HNPWA API will put "item?={id}" as the URL so need to filter those out 67 | feedItems.addAll(feed.items.where((fi) { 68 | var uri = Uri.tryParse(fi.url); 69 | return uri != null && uri.hasScheme; 70 | })); 71 | hasNextPage = feed.hasNextPage; 72 | _currentPage++; 73 | } finally { 74 | _isLoadingNextPage = false; 75 | } 76 | } 77 | 78 | @action 79 | Future _loadFirstPageStories() async { 80 | feedItems.clear(); 81 | _currentPage = 1; 82 | await loadNextPage(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/widgets/placeholder_story.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'placeholder_container.dart'; 3 | import 'styles.dart'; 4 | 5 | class PlaceholderStory extends StatefulWidget { 6 | @override 7 | _PlaceholderStoryState createState() => _PlaceholderStoryState(); 8 | } 9 | 10 | class _PlaceholderStoryState extends State 11 | with SingleTickerProviderStateMixin { 12 | Animation animation; 13 | 14 | AnimationController controller; 15 | 16 | @override 17 | void initState() { 18 | super.initState(); 19 | controller = 20 | AnimationController(duration: const Duration(seconds: 2), vsync: this); 21 | animation = Tween(begin: 0, end: 0.9).animate(controller); 22 | controller.repeat(); 23 | } 24 | 25 | @override 26 | void dispose() { 27 | controller.dispose(); 28 | super.dispose(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return InkWell( 34 | child: Padding( 35 | padding: const EdgeInsets.fromLTRB(8, 8, 8, 16), 36 | child: Row( 37 | children: [ 38 | Center( 39 | child: CircleAvatar( 40 | backgroundColor: Colors.grey, 41 | child: Center( 42 | child: Text( 43 | '', 44 | ), 45 | ), 46 | ), 47 | ), 48 | Expanded( 49 | child: Padding( 50 | padding: const EdgeInsets.fromLTRB(8, 0, 0, 0), 51 | child: Column( 52 | crossAxisAlignment: CrossAxisAlignment.stretch, 53 | children: [ 54 | TextSpacer( 55 | PlaceholderContainer( 56 | child: Text( 57 | '', 58 | style: Theme.of(context).textTheme.subtitle1, 59 | ), 60 | animation: animation, 61 | ), 62 | ), 63 | TextSpacer( 64 | PlaceholderContainer( 65 | child: Text( 66 | '', 67 | style: Theme.of(context).textTheme.subtitle2, 68 | ), 69 | animation: animation, 70 | ), 71 | ), 72 | PlaceholderContainer( 73 | child: 74 | Text('', style: Theme.of(context).textTheme.subtitle2), 75 | animation: animation, 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | ], 82 | ), 83 | )); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /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 | 43 | 44 | -------------------------------------------------------------------------------- /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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | def keystoreProperties = new Properties() 28 | def keystorePropertiesFile = rootProject.file('key.properties') 29 | if (keystorePropertiesFile.exists()) { 30 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 31 | } 32 | 33 | android { 34 | compileSdkVersion 28 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | applicationId "io.crossingthestreams.supnews" 42 | minSdkVersion 16 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | // beginning of config for store release 50 | signingConfigs { 51 | release { 52 | keyAlias keystoreProperties['keyAlias'] 53 | keyPassword keystoreProperties['keyPassword'] 54 | storeFile file(keystoreProperties['storeFile']) 55 | storePassword keystoreProperties['storePassword'] 56 | } 57 | } 58 | 59 | buildTypes { 60 | release { 61 | signingConfig signingConfigs.release 62 | minifyEnabled true 63 | useProguard true 64 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 65 | } 66 | } 67 | // end of config related to store release 68 | 69 | // for debugging 70 | /*buildTypes { 71 | release { 72 | // TODO: Add your own signing config for the release build. 73 | // Signing with the debug keys for now, so `flutter run --release` works. 74 | signingConfig signingConfigs.debug 75 | } 76 | }*/ 77 | } 78 | 79 | flutter { 80 | source '../..' 81 | } 82 | 83 | dependencies { 84 | testImplementation 'junit:junit:4.12' 85 | androidTestImplementation 'androidx.test:runner:1.1.0' 86 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 87 | } 88 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "ItunesArtwork@2x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } -------------------------------------------------------------------------------- /lib/screens/stories_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_mobx/flutter_mobx.dart'; 3 | import 'package:mobx/mobx.dart'; 4 | import 'package:incrementally_loading_listview/incrementally_loading_listview.dart'; 5 | import '../stores/stories_store.dart'; 6 | import '../widgets/placeholder_stories.dart'; 7 | import '../widgets/placeholder_story.dart'; 8 | import '../widgets/story.dart'; 9 | 10 | class StoriesPage extends StatefulWidget { 11 | final T store; 12 | StoriesPage(this.store, {Key key}) : super(key: key); 13 | 14 | @override 15 | _StoriesPageState createState() => _StoriesPageState(); 16 | } 17 | 18 | /// Notes: use of [AutomaticKeepAliveClientMixin] with the [wantKeepAlive] override will effectively allow Flutter to retain the page state, including the scroll position. 19 | /// Without it, switching back and forth between tabs would cause the data to tab to be rebuilt, which in turn causes data to be fetched etc 20 | class _StoriesPageState extends State 21 | with AutomaticKeepAliveClientMixin { 22 | @override 23 | void initState() { 24 | super.initState(); 25 | widget.store.loadInitialStories(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | super.build(context); 31 | return Observer( 32 | builder: (_) { 33 | switch (widget.store.loadFeedItemsFuture.status) { 34 | case FutureStatus.rejected: 35 | return Center( 36 | child: Column( 37 | mainAxisAlignment: MainAxisAlignment.center, 38 | children: [ 39 | Text('Oops something went wrong'), 40 | RaisedButton( 41 | child: Text('Retry'), 42 | onPressed: () async { 43 | await widget.store.retry(); 44 | }, 45 | ), 46 | ], 47 | ), 48 | ); 49 | case FutureStatus.fulfilled: 50 | return RefreshIndicator( 51 | child: IncrementallyLoadingListView( 52 | loadMore: () async { 53 | await widget.store.loadNextPage(); 54 | }, 55 | hasMore: () => widget.store.hasNextPage, 56 | itemCount: () => widget.store.feedItems.length, 57 | itemBuilder: (context, index) { 58 | if (index == widget.store.feedItems.length - 1 && 59 | widget.store.hasNextPage && 60 | !widget.store.loadingNextPage) { 61 | return Column( 62 | children: [ 63 | Story(widget.store.feedItems[index]), 64 | PlaceholderStory(), 65 | ], 66 | ); 67 | } 68 | return Story(widget.store.feedItems[index]); 69 | }, 70 | ), 71 | onRefresh: () async { 72 | await widget.store.refresh(); 73 | }, 74 | ); 75 | case FutureStatus.pending: 76 | default: 77 | return PlaceholderStories(); 78 | } 79 | }, 80 | ); 81 | } 82 | 83 | @override 84 | bool get wantKeepAlive => true; 85 | } 86 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # supnews 2 | 3 | [![Codemagic build status](https://api.codemagic.io/apps/5cd97d9341300a1c8aa14062/5cd97d9341300a1c8aa14061/status_badge.svg)](https://codemagic.io/apps/5cd97d9341300a1c8aa14062/5cd97d9341300a1c8aa14061/latest_build) 4 | 5 | A simple Flutter app for displaying stories retrieved from Hacker News. Uses the [provider](https://github.com/rrousselGit/provider) package along with [MobX](https://mobx.pub) to managing the architecture 6 | 7 | The app demonstrates 8 | 9 | * Using MobX for managing UI state where stores function as view models that may talk to other services i.e. how to follow the widget -> store -> service structure 10 | * Using `provider` organise stores and inject them into the appropriate widgets. Services are injected into the stores via `provider` as well. Both of these are done using the `Consumer` widget (and `Consumer2` when injecting in two dependencies) 11 | * Using `provider` to resolve dependencies using the static `Provider.of(context)` method 12 | * How to load more items into a `ListView` based on scroll position by invoking an action in MobX 13 | * Opening stories using the [url_launcher](https://github.com/flutter/plugins/tree/master/packages/url_launcher) package with the ability to load them in the browser instead of doing so "in-app" 14 | * Toggling between dark and light theme 15 | * Using custom fonts 16 | * Using a Cupertino widgets within a material app 17 | 18 | **NOTE**: when the app was originally written, the `ProxyProvider` class wasn't available yet in the `provider` package. It should be possible to update the app to make of use of it 19 | 20 | When trying to debug on your machine via an Android device, you'll currently need to go to android/app/build.gradle and modify the contents to comment out/remove the store release related configuration. The latter configuration is there as I currently have this setup with [Codemagic](https://codemagic.io/) to deploy to the Google Play store. The relevant sections look like as follows 21 | 22 | ``` 23 | // beginning of config for store release 24 | signingConfigs { 25 | release { 26 | keyAlias keystoreProperties['keyAlias'] 27 | keyPassword keystoreProperties['keyPassword'] 28 | storeFile file(keystoreProperties['storeFile']) 29 | storePassword keystoreProperties['storePassword'] 30 | } 31 | } 32 | 33 | buildTypes { 34 | release { 35 | signingConfig signingConfigs.release 36 | minifyEnabled true 37 | useProguard true 38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 39 | } 40 | } 41 | // end of config related to store release 42 | 43 | // for debugging 44 | /*buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | }*/ 51 | ``` 52 | 53 | After modifying it so it can be debugged, it'll look like as follows 54 | 55 | ``` 56 | // beginning of config for store release 57 | /*signingConfigs { 58 | release { 59 | keyAlias keystoreProperties['keyAlias'] 60 | keyPassword keystoreProperties['keyPassword'] 61 | storeFile file(keystoreProperties['storeFile']) 62 | storePassword keystoreProperties['storePassword'] 63 | } 64 | } 65 | 66 | buildTypes { 67 | release { 68 | signingConfig signingConfigs.release 69 | minifyEnabled true 70 | useProguard true 71 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 72 | } 73 | }*/ 74 | // end of config related to store release 75 | 76 | // for debugging 77 | buildTypes { 78 | release { 79 | // TODO: Add your own signing config for the release build. 80 | // Signing with the debug keys for now, so `flutter run --release` works. 81 | signingConfig signingConfigs.debug 82 | } 83 | } 84 | ``` 85 | 86 | ## Attributions 87 | Google Play and the Google Play logo are trademarks of Google LLC. 88 | -------------------------------------------------------------------------------- /lib/stores/stories_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'stories_store.dart'; 4 | 5 | // ************************************************************************** 6 | // StoreGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 10 | 11 | mixin _$StoriesStore on StoriesStoreBase, Store { 12 | final _$hasNextPageAtom = Atom(name: 'StoriesStoreBase.hasNextPage'); 13 | 14 | @override 15 | bool get hasNextPage { 16 | _$hasNextPageAtom.reportRead(); 17 | return super.hasNextPage; 18 | } 19 | 20 | @override 21 | set hasNextPage(bool value) { 22 | _$hasNextPageAtom.reportWrite(value, super.hasNextPage, () { 23 | super.hasNextPage = value; 24 | }); 25 | } 26 | 27 | final _$feedItemsAtom = Atom(name: 'StoriesStoreBase.feedItems'); 28 | 29 | @override 30 | ObservableList get feedItems { 31 | _$feedItemsAtom.reportRead(); 32 | return super.feedItems; 33 | } 34 | 35 | @override 36 | set feedItems(ObservableList value) { 37 | _$feedItemsAtom.reportWrite(value, super.feedItems, () { 38 | super.feedItems = value; 39 | }); 40 | } 41 | 42 | final _$loadFeedItemsFutureAtom = 43 | Atom(name: 'StoriesStoreBase.loadFeedItemsFuture'); 44 | 45 | @override 46 | ObservableFuture get loadFeedItemsFuture { 47 | _$loadFeedItemsFutureAtom.reportRead(); 48 | return super.loadFeedItemsFuture; 49 | } 50 | 51 | @override 52 | set loadFeedItemsFuture(ObservableFuture value) { 53 | _$loadFeedItemsFutureAtom.reportWrite(value, super.loadFeedItemsFuture, () { 54 | super.loadFeedItemsFuture = value; 55 | }); 56 | } 57 | 58 | final _$loadingNextPageAtom = Atom(name: 'StoriesStoreBase.loadingNextPage'); 59 | 60 | @override 61 | bool get loadingNextPage { 62 | _$loadingNextPageAtom.reportRead(); 63 | return super.loadingNextPage; 64 | } 65 | 66 | @override 67 | set loadingNextPage(bool value) { 68 | _$loadingNextPageAtom.reportWrite(value, super.loadingNextPage, () { 69 | super.loadingNextPage = value; 70 | }); 71 | } 72 | 73 | final _$loadNextPageAsyncAction = 74 | AsyncAction('StoriesStoreBase.loadNextPage'); 75 | 76 | @override 77 | Future loadNextPage() { 78 | return _$loadNextPageAsyncAction.run(() => super.loadNextPage()); 79 | } 80 | 81 | final _$_loadFirstPageStoriesAsyncAction = 82 | AsyncAction('StoriesStoreBase._loadFirstPageStories'); 83 | 84 | @override 85 | Future _loadFirstPageStories() { 86 | return _$_loadFirstPageStoriesAsyncAction 87 | .run(() => super._loadFirstPageStories()); 88 | } 89 | 90 | final _$StoriesStoreBaseActionController = 91 | ActionController(name: 'StoriesStoreBase'); 92 | 93 | @override 94 | Future refresh() { 95 | final _$actionInfo = _$StoriesStoreBaseActionController.startAction( 96 | name: 'StoriesStoreBase.refresh'); 97 | try { 98 | return super.refresh(); 99 | } finally { 100 | _$StoriesStoreBaseActionController.endAction(_$actionInfo); 101 | } 102 | } 103 | 104 | @override 105 | Future retry() { 106 | final _$actionInfo = _$StoriesStoreBaseActionController.startAction( 107 | name: 'StoriesStoreBase.retry'); 108 | try { 109 | return super.retry(); 110 | } finally { 111 | _$StoriesStoreBaseActionController.endAction(_$actionInfo); 112 | } 113 | } 114 | 115 | @override 116 | Future loadInitialStories() { 117 | final _$actionInfo = _$StoriesStoreBaseActionController.startAction( 118 | name: 'StoriesStoreBase.loadInitialStories'); 119 | try { 120 | return super.loadInitialStories(); 121 | } finally { 122 | _$StoriesStoreBaseActionController.endAction(_$actionInfo); 123 | } 124 | } 125 | 126 | @override 127 | String toString() { 128 | return ''' 129 | hasNextPage: ${hasNextPage}, 130 | feedItems: ${feedItems}, 131 | loadFeedItemsFuture: ${loadFeedItemsFuture}, 132 | loadingNextPage: ${loadingNextPage} 133 | '''; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /lib/widgets/themeable_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_mobx/flutter_mobx.dart'; 4 | import 'package:hnpwa_client/hnpwa_client.dart'; 5 | import 'package:provider/provider.dart'; 6 | import '../screens/favourites_page.dart'; 7 | import '../stores/favourites_store.dart'; 8 | import '../screens/new_stories_page.dart'; 9 | import '../screens/settings_page.dart'; 10 | import '../screens/top_stories_page.dart'; 11 | import '../services/preferences_service.dart'; 12 | import '../stores/new_stories_store.dart'; 13 | import '../stores/top_stories_store.dart'; 14 | import '../stores/settings_store.dart'; 15 | 16 | class ThemeableApp extends StatelessWidget { 17 | final SettingsStore settingsStore; 18 | ThemeableApp(this.settingsStore, {Key key}) : super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Observer( 23 | builder: (_) { 24 | var themeData = ThemeData( 25 | fontFamily: 'Lato', 26 | brightness: settingsStore.useDarkMode == true 27 | ? Brightness.dark 28 | : Brightness.light, 29 | primarySwatch: Colors.teal, 30 | textTheme: TextTheme( 31 | subtitle1: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), 32 | subtitle2: TextStyle(fontWeight: FontWeight.w300), 33 | ), 34 | ); 35 | return MaterialApp( 36 | title: 'SUpNews', 37 | theme: themeData, 38 | home: SafeArea( 39 | child: CupertinoTabScaffold( 40 | tabBar: CupertinoTabBar( 41 | items: [ 42 | BottomNavigationBarItem( 43 | icon: Icon(Icons.new_releases), 44 | title: Text('New'), 45 | ), 46 | BottomNavigationBarItem( 47 | icon: Icon(Icons.trending_up), 48 | title: Text('Top'), 49 | ), 50 | BottomNavigationBarItem( 51 | icon: Icon(Icons.favorite), 52 | title: Text('Favourites'), 53 | ), 54 | BottomNavigationBarItem( 55 | icon: Icon(Icons.settings), 56 | title: Text('Settings'), 57 | ), 58 | ], 59 | ), 60 | tabBuilder: (BuildContext context, int index) { 61 | switch (index) { 62 | case 0: 63 | return Consumer2( 64 | builder: (context, hnpwaClient, preferencesService, _) => 65 | Provider( 66 | create: (_) => 67 | NewStoriesStore(hnpwaClient, preferencesService), 68 | child: Consumer( 69 | builder: (context, value, _) => Material( 70 | child: NewStoriesPage( 71 | value, 72 | ), 73 | ), 74 | ), 75 | ), 76 | ); 77 | case 1: 78 | return Consumer2( 79 | builder: (context, hnpwaClient, preferencesService, _) => 80 | Provider( 81 | create: (_) => TopStoriesStore( 82 | hnpwaClient, 83 | preferencesService, 84 | ), 85 | child: Consumer( 86 | builder: (context, value, _) => Material( 87 | child: TopStoriesPage( 88 | value, 89 | ), 90 | ), 91 | ), 92 | ), 93 | ); 94 | case 2: 95 | return Consumer( 96 | builder: (context, value, _) => Material( 97 | child: FavouritesPage(value), 98 | ), 99 | ); 100 | case 3: 101 | return Consumer( 102 | builder: (context, value, _) => Material( 103 | child: SettingsPage(value), 104 | ), 105 | ); 106 | } 107 | return null; 108 | }, 109 | ), 110 | ), 111 | ); 112 | }, 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/widgets/story.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_mobx/flutter_mobx.dart'; 3 | import 'package:hnpwa_client/hnpwa_client.dart'; 4 | import 'package:intl/intl.dart' show DateFormat; 5 | import 'package:provider/provider.dart'; 6 | import '../services/story_service.dart'; 7 | import '../services/sharing_service.dart'; 8 | import '../stores/favourites_store.dart'; 9 | 10 | import 'styles.dart'; 11 | 12 | class Story extends StatelessWidget { 13 | final FeedItem _item; 14 | Story(this._item, {Key key}) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final storyService = Provider.of(context); 19 | final sharingService = Provider.of(context); 20 | final favouritesStore = Provider.of(context); 21 | return InkWell( 22 | child: Padding( 23 | padding: const EdgeInsets.fromLTRB(8, 8, 8, 16), 24 | child: Row( 25 | children: [ 26 | Center( 27 | child: CircleAvatar( 28 | child: Center( 29 | child: Text( 30 | _item.points.toString(), 31 | ), 32 | ), 33 | ), 34 | ), 35 | Expanded( 36 | child: Padding( 37 | padding: const EdgeInsets.fromLTRB(8, 0, 0, 0), 38 | child: Column( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | TextSpacer( 42 | Text(_item.title, 43 | style: Theme.of(context).textTheme.subtitle1), 44 | ), 45 | TextSpacer( 46 | Text( 47 | _item.url, 48 | overflow: TextOverflow.ellipsis, 49 | style: Theme.of(context).textTheme.subtitle2, 50 | ), 51 | ), 52 | TextSpacer( 53 | Text( 54 | '${_item.user} - ${DateFormat().format( 55 | DateTime.fromMillisecondsSinceEpoch( 56 | _item.time * 1000), 57 | )}', 58 | style: Theme.of(context).textTheme.subtitle2, 59 | ), 60 | ), 61 | Text( 62 | '${_item.commentsCount} ${_item.commentsCount == 1 ? 'comment' : 'comments'}', 63 | style: Theme.of(context).textTheme.subtitle2, 64 | ), 65 | ], 66 | ), 67 | ), 68 | ), 69 | ], 70 | ), 71 | ), 72 | onTap: () async { 73 | await storyService.open(_item.url); 74 | }, 75 | onLongPress: () { 76 | showModalBottomSheet( 77 | context: context, 78 | builder: (BuildContext bc) { 79 | return Observer( 80 | builder: (_) => Container( 81 | child: new Wrap( 82 | children: [ 83 | new ListTile( 84 | leading: new Icon(Icons.favorite), 85 | title: new Text(favouritesStore.isInFavourites(_item) 86 | ? 'Remove from favourites' 87 | : 'Add to favourites'), 88 | onTap: () { 89 | favouritesStore.isInFavourites(_item) 90 | ? favouritesStore.removeFavourite(_item) 91 | : favouritesStore.addFavourite(_item); 92 | Navigator.of(context).pop(); 93 | }, 94 | ), 95 | new ListTile( 96 | leading: new Icon(Icons.open_in_browser), 97 | title: new Text('Open in browser'), 98 | onTap: () async { 99 | await storyService.openInBrowser(_item.url); 100 | Navigator.of(context).pop(); 101 | }, 102 | ), 103 | if (_item.url != null) 104 | new ListTile( 105 | leading: new Icon(Icons.share), 106 | title: new Text('Share'), 107 | onTap: () async { 108 | await sharingService.share(_item.url); 109 | Navigator.of(context).pop(); 110 | }, 111 | ), 112 | ], 113 | ), 114 | ), 115 | ); 116 | }); 117 | }, 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "6.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.39.14" 18 | archive: 19 | dependency: transitive 20 | description: 21 | name: archive 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.0.13" 25 | args: 26 | dependency: transitive 27 | description: 28 | name: args 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.6.0" 32 | async: 33 | dependency: "direct main" 34 | description: 35 | name: async 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.4.1" 39 | boolean_selector: 40 | dependency: transitive 41 | description: 42 | name: boolean_selector 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.0.0" 46 | build: 47 | dependency: transitive 48 | description: 49 | name: build 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.3.0" 53 | build_config: 54 | dependency: transitive 55 | description: 56 | name: build_config 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.4.2" 60 | build_daemon: 61 | dependency: transitive 62 | description: 63 | name: build_daemon 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.1.4" 67 | build_resolvers: 68 | dependency: transitive 69 | description: 70 | name: build_resolvers 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.3.10" 74 | build_runner: 75 | dependency: "direct dev" 76 | description: 77 | name: build_runner 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.10.0" 81 | build_runner_core: 82 | dependency: transitive 83 | description: 84 | name: build_runner_core 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "5.2.0" 88 | built_collection: 89 | dependency: transitive 90 | description: 91 | name: built_collection 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "4.3.2" 95 | built_value: 96 | dependency: transitive 97 | description: 98 | name: built_value 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "7.1.0" 102 | charcode: 103 | dependency: transitive 104 | description: 105 | name: charcode 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.1.3" 109 | checked_yaml: 110 | dependency: transitive 111 | description: 112 | name: checked_yaml 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.0.2" 116 | cli_util: 117 | dependency: transitive 118 | description: 119 | name: cli_util 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "0.1.4" 123 | code_builder: 124 | dependency: transitive 125 | description: 126 | name: code_builder 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "3.4.0" 130 | collection: 131 | dependency: transitive 132 | description: 133 | name: collection 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "1.14.12" 137 | convert: 138 | dependency: transitive 139 | description: 140 | name: convert 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "2.1.1" 144 | crypto: 145 | dependency: transitive 146 | description: 147 | name: crypto 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "2.1.4" 151 | csslib: 152 | dependency: transitive 153 | description: 154 | name: csslib 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "0.16.1" 158 | cupertino_icons: 159 | dependency: "direct main" 160 | description: 161 | name: cupertino_icons 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "0.1.3" 165 | dart_style: 166 | dependency: transitive 167 | description: 168 | name: dart_style 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "1.3.6" 172 | file: 173 | dependency: transitive 174 | description: 175 | name: file 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "5.2.1" 179 | fixnum: 180 | dependency: transitive 181 | description: 182 | name: fixnum 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "0.10.11" 186 | flutter: 187 | dependency: "direct main" 188 | description: flutter 189 | source: sdk 190 | version: "0.0.0" 191 | flutter_mobx: 192 | dependency: "direct main" 193 | description: 194 | name: flutter_mobx 195 | url: "https://pub.dartlang.org" 196 | source: hosted 197 | version: "1.1.0+1" 198 | flutter_statusbarcolor: 199 | dependency: "direct main" 200 | description: 201 | name: flutter_statusbarcolor 202 | url: "https://pub.dartlang.org" 203 | source: hosted 204 | version: "0.2.3" 205 | flutter_test: 206 | dependency: "direct dev" 207 | description: flutter 208 | source: sdk 209 | version: "0.0.0" 210 | flutter_web_plugins: 211 | dependency: transitive 212 | description: flutter 213 | source: sdk 214 | version: "0.0.0" 215 | glob: 216 | dependency: transitive 217 | description: 218 | name: glob 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "1.2.0" 222 | graphs: 223 | dependency: transitive 224 | description: 225 | name: graphs 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "0.2.0" 229 | hnpwa_client: 230 | dependency: "direct main" 231 | description: 232 | name: hnpwa_client 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "2.0.0" 236 | html: 237 | dependency: transitive 238 | description: 239 | name: html 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "0.14.0+3" 243 | http: 244 | dependency: "direct main" 245 | description: 246 | name: http 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "0.12.2" 250 | http_multi_server: 251 | dependency: transitive 252 | description: 253 | name: http_multi_server 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "2.2.0" 257 | http_parser: 258 | dependency: transitive 259 | description: 260 | name: http_parser 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "3.1.4" 264 | image: 265 | dependency: transitive 266 | description: 267 | name: image 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "2.1.12" 271 | incrementally_loading_listview: 272 | dependency: "direct main" 273 | description: 274 | name: incrementally_loading_listview 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "0.1.0+1" 278 | intl: 279 | dependency: "direct main" 280 | description: 281 | name: intl 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "0.15.8" 285 | io: 286 | dependency: transitive 287 | description: 288 | name: io 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "0.3.4" 292 | js: 293 | dependency: transitive 294 | description: 295 | name: js 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "0.6.2" 299 | json_annotation: 300 | dependency: transitive 301 | description: 302 | name: json_annotation 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "3.0.1" 306 | logging: 307 | dependency: transitive 308 | description: 309 | name: logging 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "0.11.4" 313 | matcher: 314 | dependency: transitive 315 | description: 316 | name: matcher 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "0.12.6" 320 | meta: 321 | dependency: transitive 322 | description: 323 | name: meta 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "1.1.8" 327 | mime: 328 | dependency: transitive 329 | description: 330 | name: mime 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "0.9.6+3" 334 | mobx: 335 | dependency: "direct main" 336 | description: 337 | name: mobx 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "1.2.1+1" 341 | mobx_codegen: 342 | dependency: "direct dev" 343 | description: 344 | name: mobx_codegen 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "1.1.0+1" 348 | node_interop: 349 | dependency: transitive 350 | description: 351 | name: node_interop 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "1.1.1" 355 | node_io: 356 | dependency: transitive 357 | description: 358 | name: node_io 359 | url: "https://pub.dartlang.org" 360 | source: hosted 361 | version: "1.1.1" 362 | package_config: 363 | dependency: transitive 364 | description: 365 | name: package_config 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "1.9.3" 369 | path: 370 | dependency: transitive 371 | description: 372 | name: path 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "1.6.4" 376 | path_provider_linux: 377 | dependency: transitive 378 | description: 379 | name: path_provider_linux 380 | url: "https://pub.dartlang.org" 381 | source: hosted 382 | version: "0.0.1+2" 383 | path_provider_platform_interface: 384 | dependency: transitive 385 | description: 386 | name: path_provider_platform_interface 387 | url: "https://pub.dartlang.org" 388 | source: hosted 389 | version: "1.0.2" 390 | pedantic: 391 | dependency: transitive 392 | description: 393 | name: pedantic 394 | url: "https://pub.dartlang.org" 395 | source: hosted 396 | version: "1.9.0" 397 | petitparser: 398 | dependency: transitive 399 | description: 400 | name: petitparser 401 | url: "https://pub.dartlang.org" 402 | source: hosted 403 | version: "2.4.0" 404 | platform: 405 | dependency: transitive 406 | description: 407 | name: platform 408 | url: "https://pub.dartlang.org" 409 | source: hosted 410 | version: "2.2.1" 411 | platform_detect: 412 | dependency: transitive 413 | description: 414 | name: platform_detect 415 | url: "https://pub.dartlang.org" 416 | source: hosted 417 | version: "1.4.0" 418 | plugin_platform_interface: 419 | dependency: transitive 420 | description: 421 | name: plugin_platform_interface 422 | url: "https://pub.dartlang.org" 423 | source: hosted 424 | version: "1.0.2" 425 | pool: 426 | dependency: transitive 427 | description: 428 | name: pool 429 | url: "https://pub.dartlang.org" 430 | source: hosted 431 | version: "1.4.0" 432 | process: 433 | dependency: transitive 434 | description: 435 | name: process 436 | url: "https://pub.dartlang.org" 437 | source: hosted 438 | version: "3.0.13" 439 | provider: 440 | dependency: "direct main" 441 | description: 442 | name: provider 443 | url: "https://pub.dartlang.org" 444 | source: hosted 445 | version: "3.2.0" 446 | pub_semver: 447 | dependency: transitive 448 | description: 449 | name: pub_semver 450 | url: "https://pub.dartlang.org" 451 | source: hosted 452 | version: "1.4.4" 453 | pubspec_parse: 454 | dependency: transitive 455 | description: 456 | name: pubspec_parse 457 | url: "https://pub.dartlang.org" 458 | source: hosted 459 | version: "0.1.5" 460 | quiver: 461 | dependency: transitive 462 | description: 463 | name: quiver 464 | url: "https://pub.dartlang.org" 465 | source: hosted 466 | version: "2.1.3" 467 | rxdart: 468 | dependency: transitive 469 | description: 470 | name: rxdart 471 | url: "https://pub.dartlang.org" 472 | source: hosted 473 | version: "0.21.0" 474 | share: 475 | dependency: "direct main" 476 | description: 477 | name: share 478 | url: "https://pub.dartlang.org" 479 | source: hosted 480 | version: "0.6.4+3" 481 | shared_preferences: 482 | dependency: "direct main" 483 | description: 484 | name: shared_preferences 485 | url: "https://pub.dartlang.org" 486 | source: hosted 487 | version: "0.5.8" 488 | shared_preferences_linux: 489 | dependency: transitive 490 | description: 491 | name: shared_preferences_linux 492 | url: "https://pub.dartlang.org" 493 | source: hosted 494 | version: "0.0.2+1" 495 | shared_preferences_macos: 496 | dependency: transitive 497 | description: 498 | name: shared_preferences_macos 499 | url: "https://pub.dartlang.org" 500 | source: hosted 501 | version: "0.0.1+10" 502 | shared_preferences_platform_interface: 503 | dependency: transitive 504 | description: 505 | name: shared_preferences_platform_interface 506 | url: "https://pub.dartlang.org" 507 | source: hosted 508 | version: "1.0.4" 509 | shared_preferences_web: 510 | dependency: transitive 511 | description: 512 | name: shared_preferences_web 513 | url: "https://pub.dartlang.org" 514 | source: hosted 515 | version: "0.1.2+7" 516 | shelf: 517 | dependency: transitive 518 | description: 519 | name: shelf 520 | url: "https://pub.dartlang.org" 521 | source: hosted 522 | version: "0.7.7" 523 | shelf_web_socket: 524 | dependency: transitive 525 | description: 526 | name: shelf_web_socket 527 | url: "https://pub.dartlang.org" 528 | source: hosted 529 | version: "0.2.3" 530 | sky_engine: 531 | dependency: transitive 532 | description: flutter 533 | source: sdk 534 | version: "0.0.99" 535 | source_gen: 536 | dependency: transitive 537 | description: 538 | name: source_gen 539 | url: "https://pub.dartlang.org" 540 | source: hosted 541 | version: "0.9.6" 542 | source_span: 543 | dependency: transitive 544 | description: 545 | name: source_span 546 | url: "https://pub.dartlang.org" 547 | source: hosted 548 | version: "1.7.0" 549 | stack_trace: 550 | dependency: transitive 551 | description: 552 | name: stack_trace 553 | url: "https://pub.dartlang.org" 554 | source: hosted 555 | version: "1.9.3" 556 | stream_channel: 557 | dependency: transitive 558 | description: 559 | name: stream_channel 560 | url: "https://pub.dartlang.org" 561 | source: hosted 562 | version: "2.0.0" 563 | stream_transform: 564 | dependency: transitive 565 | description: 566 | name: stream_transform 567 | url: "https://pub.dartlang.org" 568 | source: hosted 569 | version: "1.2.0" 570 | string_scanner: 571 | dependency: transitive 572 | description: 573 | name: string_scanner 574 | url: "https://pub.dartlang.org" 575 | source: hosted 576 | version: "1.0.5" 577 | term_glyph: 578 | dependency: transitive 579 | description: 580 | name: term_glyph 581 | url: "https://pub.dartlang.org" 582 | source: hosted 583 | version: "1.1.0" 584 | test_api: 585 | dependency: transitive 586 | description: 587 | name: test_api 588 | url: "https://pub.dartlang.org" 589 | source: hosted 590 | version: "0.2.15" 591 | timing: 592 | dependency: transitive 593 | description: 594 | name: timing 595 | url: "https://pub.dartlang.org" 596 | source: hosted 597 | version: "0.1.1+2" 598 | typed_data: 599 | dependency: transitive 600 | description: 601 | name: typed_data 602 | url: "https://pub.dartlang.org" 603 | source: hosted 604 | version: "1.1.6" 605 | url_launcher: 606 | dependency: "direct main" 607 | description: 608 | name: url_launcher 609 | url: "https://pub.dartlang.org" 610 | source: hosted 611 | version: "5.5.0" 612 | url_launcher_linux: 613 | dependency: transitive 614 | description: 615 | name: url_launcher_linux 616 | url: "https://pub.dartlang.org" 617 | source: hosted 618 | version: "0.0.1+1" 619 | url_launcher_macos: 620 | dependency: transitive 621 | description: 622 | name: url_launcher_macos 623 | url: "https://pub.dartlang.org" 624 | source: hosted 625 | version: "0.0.1+7" 626 | url_launcher_platform_interface: 627 | dependency: transitive 628 | description: 629 | name: url_launcher_platform_interface 630 | url: "https://pub.dartlang.org" 631 | source: hosted 632 | version: "1.0.7" 633 | url_launcher_web: 634 | dependency: transitive 635 | description: 636 | name: url_launcher_web 637 | url: "https://pub.dartlang.org" 638 | source: hosted 639 | version: "0.1.2" 640 | vector_math: 641 | dependency: transitive 642 | description: 643 | name: vector_math 644 | url: "https://pub.dartlang.org" 645 | source: hosted 646 | version: "2.0.8" 647 | watcher: 648 | dependency: transitive 649 | description: 650 | name: watcher 651 | url: "https://pub.dartlang.org" 652 | source: hosted 653 | version: "0.9.7+15" 654 | web_socket_channel: 655 | dependency: transitive 656 | description: 657 | name: web_socket_channel 658 | url: "https://pub.dartlang.org" 659 | source: hosted 660 | version: "1.1.0" 661 | xdg_directories: 662 | dependency: transitive 663 | description: 664 | name: xdg_directories 665 | url: "https://pub.dartlang.org" 666 | source: hosted 667 | version: "0.1.0" 668 | xml: 669 | dependency: transitive 670 | description: 671 | name: xml 672 | url: "https://pub.dartlang.org" 673 | source: hosted 674 | version: "3.6.1" 675 | yaml: 676 | dependency: transitive 677 | description: 678 | name: yaml 679 | url: "https://pub.dartlang.org" 680 | source: hosted 681 | version: "2.2.1" 682 | sdks: 683 | dart: ">=2.7.0 <3.0.0" 684 | flutter: ">=1.12.13+hotfix.5 <2.0.0" 685 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 13 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 14 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | 9A1CCE967A82656C5F25E294 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 08C990C1D9D1273D0C00AA07 /* libPods-Runner.a */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = ""; 26 | dstSubfolderSpec = 10; 27 | files = ( 28 | ); 29 | name = "Embed Frameworks"; 30 | runOnlyForDeploymentPostprocessing = 0; 31 | }; 32 | /* End PBXCopyFilesBuildPhase section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 08C990C1D9D1273D0C00AA07 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 37 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 3B937C7E2CAF749EB3B220C0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 40 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 41 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 42 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 43 | 816F7B3FCBE2659DD274F6F2 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 44 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 45 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 46 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 48 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 51 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | B82F2810F74EB960A446CD9A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 9A1CCE967A82656C5F25E294 /* libPods-Runner.a in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 00AD941F10E0F8DF11A02EE6 /* Frameworks */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 08C990C1D9D1273D0C00AA07 /* libPods-Runner.a */, 71 | ); 72 | name = Frameworks; 73 | sourceTree = ""; 74 | }; 75 | 9740EEB11CF90186004384FC /* Flutter */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 79 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 80 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 81 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 82 | ); 83 | name = Flutter; 84 | sourceTree = ""; 85 | }; 86 | 97C146E51CF9000F007C117D = { 87 | isa = PBXGroup; 88 | children = ( 89 | 9740EEB11CF90186004384FC /* Flutter */, 90 | 97C146F01CF9000F007C117D /* Runner */, 91 | 97C146EF1CF9000F007C117D /* Products */, 92 | ED0AEBFB8C7436680B492D2F /* Pods */, 93 | 00AD941F10E0F8DF11A02EE6 /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 97C146EF1CF9000F007C117D /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 97C146EE1CF9000F007C117D /* Runner.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 97C146F01CF9000F007C117D /* Runner */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 109 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 110 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 111 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 112 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 113 | 97C147021CF9000F007C117D /* Info.plist */, 114 | 97C146F11CF9000F007C117D /* Supporting Files */, 115 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 116 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 97C146F21CF9000F007C117D /* main.m */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | ED0AEBFB8C7436680B492D2F /* Pods */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 3B937C7E2CAF749EB3B220C0 /* Pods-Runner.debug.xcconfig */, 133 | 816F7B3FCBE2659DD274F6F2 /* Pods-Runner.release.xcconfig */, 134 | B82F2810F74EB960A446CD9A /* Pods-Runner.profile.xcconfig */, 135 | ); 136 | name = Pods; 137 | sourceTree = ""; 138 | }; 139 | /* End PBXGroup section */ 140 | 141 | /* Begin PBXNativeTarget section */ 142 | 97C146ED1CF9000F007C117D /* Runner */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 145 | buildPhases = ( 146 | 3B910D0D34250D1D645C5114 /* [CP] Check Pods Manifest.lock */, 147 | 9740EEB61CF901F6004384FC /* Run Script */, 148 | 97C146EA1CF9000F007C117D /* Sources */, 149 | 97C146EB1CF9000F007C117D /* Frameworks */, 150 | 97C146EC1CF9000F007C117D /* Resources */, 151 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 152 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 153 | 7AB8194D761BB4D4D0B532C5 /* [CP] Embed Pods Frameworks */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | ); 159 | name = Runner; 160 | productName = Runner; 161 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 162 | productType = "com.apple.product-type.application"; 163 | }; 164 | /* End PBXNativeTarget section */ 165 | 166 | /* Begin PBXProject section */ 167 | 97C146E61CF9000F007C117D /* Project object */ = { 168 | isa = PBXProject; 169 | attributes = { 170 | LastUpgradeCheck = 0910; 171 | ORGANIZATIONNAME = "The Chromium Authors"; 172 | TargetAttributes = { 173 | 97C146ED1CF9000F007C117D = { 174 | CreatedOnToolsVersion = 7.3.1; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = English; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | English, 184 | en, 185 | Base, 186 | ); 187 | mainGroup = 97C146E51CF9000F007C117D; 188 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 189 | projectDirPath = ""; 190 | projectRoot = ""; 191 | targets = ( 192 | 97C146ED1CF9000F007C117D /* Runner */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXResourcesBuildPhase section */ 198 | 97C146EC1CF9000F007C117D /* Resources */ = { 199 | isa = PBXResourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 203 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 204 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 205 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 206 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXResourcesBuildPhase section */ 211 | 212 | /* Begin PBXShellScriptBuildPhase section */ 213 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 214 | isa = PBXShellScriptBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | ); 218 | inputPaths = ( 219 | ); 220 | name = "Thin Binary"; 221 | outputPaths = ( 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | shellPath = /bin/sh; 225 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 226 | }; 227 | 3B910D0D34250D1D645C5114 /* [CP] Check Pods Manifest.lock */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 234 | "${PODS_ROOT}/Manifest.lock", 235 | ); 236 | name = "[CP] Check Pods Manifest.lock"; 237 | outputPaths = ( 238 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | shellPath = /bin/sh; 242 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 243 | showEnvVarsInLog = 0; 244 | }; 245 | 7AB8194D761BB4D4D0B532C5 /* [CP] Embed Pods Frameworks */ = { 246 | isa = PBXShellScriptBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | ); 250 | inputPaths = ( 251 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 252 | "${PODS_ROOT}/../Flutter/Flutter.framework", 253 | ); 254 | name = "[CP] Embed Pods Frameworks"; 255 | outputPaths = ( 256 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 285 | 97C146F31CF9000F007C117D /* main.m in Sources */, 286 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXSourcesBuildPhase section */ 291 | 292 | /* Begin PBXVariantGroup section */ 293 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 294 | isa = PBXVariantGroup; 295 | children = ( 296 | 97C146FB1CF9000F007C117D /* Base */, 297 | ); 298 | name = Main.storyboard; 299 | sourceTree = ""; 300 | }; 301 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 97C147001CF9000F007C117D /* Base */, 305 | ); 306 | name = LaunchScreen.storyboard; 307 | sourceTree = ""; 308 | }; 309 | /* End PBXVariantGroup section */ 310 | 311 | /* Begin XCBuildConfiguration section */ 312 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 313 | isa = XCBuildConfiguration; 314 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 315 | buildSettings = { 316 | ALWAYS_SEARCH_USER_PATHS = NO; 317 | CLANG_ANALYZER_NONNULL = YES; 318 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 319 | CLANG_CXX_LIBRARY = "libc++"; 320 | CLANG_ENABLE_MODULES = YES; 321 | CLANG_ENABLE_OBJC_ARC = YES; 322 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 323 | CLANG_WARN_BOOL_CONVERSION = YES; 324 | CLANG_WARN_COMMA = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | TARGETED_DEVICE_FAMILY = "1,2"; 356 | VALIDATE_PRODUCT = YES; 357 | }; 358 | name = Profile; 359 | }; 360 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 361 | isa = XCBuildConfiguration; 362 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 363 | buildSettings = { 364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 365 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 366 | DEVELOPMENT_TEAM = S8QB4VV633; 367 | ENABLE_BITCODE = NO; 368 | FRAMEWORK_SEARCH_PATHS = ( 369 | "$(inherited)", 370 | "$(PROJECT_DIR)/Flutter", 371 | ); 372 | INFOPLIST_FILE = Runner/Info.plist; 373 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 374 | LIBRARY_SEARCH_PATHS = ( 375 | "$(inherited)", 376 | "$(PROJECT_DIR)/Flutter", 377 | ); 378 | PRODUCT_BUNDLE_IDENTIFIER = io.crossingthestreams.supnews; 379 | PRODUCT_NAME = "$(TARGET_NAME)"; 380 | VERSIONING_SYSTEM = "apple-generic"; 381 | }; 382 | name = Profile; 383 | }; 384 | 97C147031CF9000F007C117D /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 387 | buildSettings = { 388 | ALWAYS_SEARCH_USER_PATHS = NO; 389 | CLANG_ANALYZER_NONNULL = YES; 390 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 391 | CLANG_CXX_LIBRARY = "libc++"; 392 | CLANG_ENABLE_MODULES = YES; 393 | CLANG_ENABLE_OBJC_ARC = YES; 394 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 395 | CLANG_WARN_BOOL_CONVERSION = YES; 396 | CLANG_WARN_COMMA = YES; 397 | CLANG_WARN_CONSTANT_CONVERSION = YES; 398 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 399 | CLANG_WARN_EMPTY_BODY = YES; 400 | CLANG_WARN_ENUM_CONVERSION = YES; 401 | CLANG_WARN_INFINITE_RECURSION = YES; 402 | CLANG_WARN_INT_CONVERSION = YES; 403 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 405 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 406 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 407 | CLANG_WARN_STRICT_PROTOTYPES = YES; 408 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 409 | CLANG_WARN_UNREACHABLE_CODE = YES; 410 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 411 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 412 | COPY_PHASE_STRIP = NO; 413 | DEBUG_INFORMATION_FORMAT = dwarf; 414 | ENABLE_STRICT_OBJC_MSGSEND = YES; 415 | ENABLE_TESTABILITY = YES; 416 | GCC_C_LANGUAGE_STANDARD = gnu99; 417 | GCC_DYNAMIC_NO_PIC = NO; 418 | GCC_NO_COMMON_BLOCKS = YES; 419 | GCC_OPTIMIZATION_LEVEL = 0; 420 | GCC_PREPROCESSOR_DEFINITIONS = ( 421 | "DEBUG=1", 422 | "$(inherited)", 423 | ); 424 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 425 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 426 | GCC_WARN_UNDECLARED_SELECTOR = YES; 427 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 428 | GCC_WARN_UNUSED_FUNCTION = YES; 429 | GCC_WARN_UNUSED_VARIABLE = YES; 430 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 431 | MTL_ENABLE_DEBUG_INFO = YES; 432 | ONLY_ACTIVE_ARCH = YES; 433 | SDKROOT = iphoneos; 434 | TARGETED_DEVICE_FAMILY = "1,2"; 435 | }; 436 | name = Debug; 437 | }; 438 | 97C147041CF9000F007C117D /* Release */ = { 439 | isa = XCBuildConfiguration; 440 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 441 | buildSettings = { 442 | ALWAYS_SEARCH_USER_PATHS = NO; 443 | CLANG_ANALYZER_NONNULL = YES; 444 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 445 | CLANG_CXX_LIBRARY = "libc++"; 446 | CLANG_ENABLE_MODULES = YES; 447 | CLANG_ENABLE_OBJC_ARC = YES; 448 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 449 | CLANG_WARN_BOOL_CONVERSION = YES; 450 | CLANG_WARN_COMMA = YES; 451 | CLANG_WARN_CONSTANT_CONVERSION = YES; 452 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 453 | CLANG_WARN_EMPTY_BODY = YES; 454 | CLANG_WARN_ENUM_CONVERSION = YES; 455 | CLANG_WARN_INFINITE_RECURSION = YES; 456 | CLANG_WARN_INT_CONVERSION = YES; 457 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 460 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 461 | CLANG_WARN_STRICT_PROTOTYPES = YES; 462 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 463 | CLANG_WARN_UNREACHABLE_CODE = YES; 464 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 465 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 466 | COPY_PHASE_STRIP = NO; 467 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 468 | ENABLE_NS_ASSERTIONS = NO; 469 | ENABLE_STRICT_OBJC_MSGSEND = YES; 470 | GCC_C_LANGUAGE_STANDARD = gnu99; 471 | GCC_NO_COMMON_BLOCKS = YES; 472 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 473 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 474 | GCC_WARN_UNDECLARED_SELECTOR = YES; 475 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 476 | GCC_WARN_UNUSED_FUNCTION = YES; 477 | GCC_WARN_UNUSED_VARIABLE = YES; 478 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 479 | MTL_ENABLE_DEBUG_INFO = NO; 480 | SDKROOT = iphoneos; 481 | TARGETED_DEVICE_FAMILY = "1,2"; 482 | VALIDATE_PRODUCT = YES; 483 | }; 484 | name = Release; 485 | }; 486 | 97C147061CF9000F007C117D /* Debug */ = { 487 | isa = XCBuildConfiguration; 488 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 489 | buildSettings = { 490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 491 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 492 | ENABLE_BITCODE = NO; 493 | FRAMEWORK_SEARCH_PATHS = ( 494 | "$(inherited)", 495 | "$(PROJECT_DIR)/Flutter", 496 | ); 497 | INFOPLIST_FILE = Runner/Info.plist; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 499 | LIBRARY_SEARCH_PATHS = ( 500 | "$(inherited)", 501 | "$(PROJECT_DIR)/Flutter", 502 | ); 503 | PRODUCT_BUNDLE_IDENTIFIER = io.crossingthestreams.supnews; 504 | PRODUCT_NAME = "$(TARGET_NAME)"; 505 | VERSIONING_SYSTEM = "apple-generic"; 506 | }; 507 | name = Debug; 508 | }; 509 | 97C147071CF9000F007C117D /* Release */ = { 510 | isa = XCBuildConfiguration; 511 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 512 | buildSettings = { 513 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 514 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 515 | ENABLE_BITCODE = NO; 516 | FRAMEWORK_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(PROJECT_DIR)/Flutter", 519 | ); 520 | INFOPLIST_FILE = Runner/Info.plist; 521 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 522 | LIBRARY_SEARCH_PATHS = ( 523 | "$(inherited)", 524 | "$(PROJECT_DIR)/Flutter", 525 | ); 526 | PRODUCT_BUNDLE_IDENTIFIER = io.crossingthestreams.supnews; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | VERSIONING_SYSTEM = "apple-generic"; 529 | }; 530 | name = Release; 531 | }; 532 | /* End XCBuildConfiguration section */ 533 | 534 | /* Begin XCConfigurationList section */ 535 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 536 | isa = XCConfigurationList; 537 | buildConfigurations = ( 538 | 97C147031CF9000F007C117D /* Debug */, 539 | 97C147041CF9000F007C117D /* Release */, 540 | 249021D3217E4FDB00AE95B9 /* Profile */, 541 | ); 542 | defaultConfigurationIsVisible = 0; 543 | defaultConfigurationName = Release; 544 | }; 545 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 97C147061CF9000F007C117D /* Debug */, 549 | 97C147071CF9000F007C117D /* Release */, 550 | 249021D4217E4FDB00AE95B9 /* Profile */, 551 | ); 552 | defaultConfigurationIsVisible = 0; 553 | defaultConfigurationName = Release; 554 | }; 555 | /* End XCConfigurationList section */ 556 | }; 557 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 558 | } 559 | --------------------------------------------------------------------------------