diff --git a/README.md b/README.md
index 641d384..e057c05 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,8 @@
-# BuiltInFlutter-Task-4
\ No newline at end of file
+# BuiltInFlutter-Task-4
+## Alpha News
+
+
+
+
+
+
diff --git a/news/.gitignore b/news/.gitignore
new file mode 100644
index 0000000..0fa6b67
--- /dev/null
+++ b/news/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/news/.metadata b/news/.metadata
new file mode 100644
index 0000000..0f055bf
--- /dev/null
+++ b/news/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: ffb2ecea5223acdd139a5039be2f9c796962833d
+ channel: stable
+
+project_type: app
diff --git a/news/Adhil Roshan T/Alpha.News.V1.0.0.apk b/news/Adhil Roshan T/Alpha.News.V1.0.0.apk
new file mode 100644
index 0000000..e5cf5e1
Binary files /dev/null and b/news/Adhil Roshan T/Alpha.News.V1.0.0.apk differ
diff --git a/news/Adhil Roshan T/Details Page.png b/news/Adhil Roshan T/Details Page.png
new file mode 100644
index 0000000..6b6f50f
Binary files /dev/null and b/news/Adhil Roshan T/Details Page.png differ
diff --git a/news/Adhil Roshan T/Home Page.png b/news/Adhil Roshan T/Home Page.png
new file mode 100644
index 0000000..e68acd1
Binary files /dev/null and b/news/Adhil Roshan T/Home Page.png differ
diff --git a/news/Adhil Roshan T/Profile Page.png b/news/Adhil Roshan T/Profile Page.png
new file mode 100644
index 0000000..c962c3d
Binary files /dev/null and b/news/Adhil Roshan T/Profile Page.png differ
diff --git a/news/analysis_options.yaml b/news/analysis_options.yaml
new file mode 100644
index 0000000..61b6c4d
--- /dev/null
+++ b/news/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/news/android/.gitignore b/news/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/news/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/news/android/app/build.gradle b/news/android/app/build.gradle
new file mode 100644
index 0000000..69dae44
--- /dev/null
+++ b/news/android/app/build.gradle
@@ -0,0 +1,68 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 30
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.example.news"
+ minSdkVersion 19
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/news/android/app/src/debug/AndroidManifest.xml b/news/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..169884f
--- /dev/null
+++ b/news/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/news/android/app/src/main/AndroidManifest.xml b/news/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d11613f
--- /dev/null
+++ b/news/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/news/android/app/src/main/kotlin/com/example/news/MainActivity.kt b/news/android/app/src/main/kotlin/com/example/news/MainActivity.kt
new file mode 100644
index 0000000..3777cd4
--- /dev/null
+++ b/news/android/app/src/main/kotlin/com/example/news/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.example.news
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/news/android/app/src/main/res/drawable-v21/launch_background.xml b/news/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/news/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/news/android/app/src/main/res/drawable/launch_background.xml b/news/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/news/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/news/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/news/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/news/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/news/android/app/src/main/res/mipmap-hdpi/launcher_icon.png
new file mode 100644
index 0000000..972174b
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ
diff --git a/news/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/news/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/news/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/news/android/app/src/main/res/mipmap-mdpi/launcher_icon.png
new file mode 100644
index 0000000..b3fea1a
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ
diff --git a/news/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/news/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/news/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/news/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
new file mode 100644
index 0000000..6681856
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ
diff --git a/news/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/news/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/news/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/news/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
new file mode 100644
index 0000000..3676c30
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ
diff --git a/news/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/news/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/news/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/news/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
new file mode 100644
index 0000000..a923aaa
Binary files /dev/null and b/news/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ
diff --git a/news/android/app/src/main/res/values-night/styles.xml b/news/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..449a9f9
--- /dev/null
+++ b/news/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/news/android/app/src/main/res/values/styles.xml b/news/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d74aa35
--- /dev/null
+++ b/news/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/news/android/app/src/profile/AndroidManifest.xml b/news/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..169884f
--- /dev/null
+++ b/news/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/news/android/build.gradle b/news/android/build.gradle
new file mode 100644
index 0000000..ed45c65
--- /dev/null
+++ b/news/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/news/android/gradle.properties b/news/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/news/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/news/android/gradle/wrapper/gradle-wrapper.properties b/news/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bc6a58a
--- /dev/null
+++ b/news/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
diff --git a/news/android/settings.gradle b/news/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/news/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/news/assets/icon.png b/news/assets/icon.png
new file mode 100644
index 0000000..c216779
Binary files /dev/null and b/news/assets/icon.png differ
diff --git a/news/ios/.gitignore b/news/ios/.gitignore
new file mode 100644
index 0000000..151026b
--- /dev/null
+++ b/news/ios/.gitignore
@@ -0,0 +1,33 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/news/ios/Flutter/AppFrameworkInfo.plist b/news/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..8d4492f
--- /dev/null
+++ b/news/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 9.0
+
+
diff --git a/news/ios/Flutter/Debug.xcconfig b/news/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/news/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/news/ios/Flutter/Release.xcconfig b/news/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/news/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/news/ios/Runner.xcodeproj/project.pbxproj b/news/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..fd0423f
--- /dev/null
+++ b/news/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,471 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1020;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.news;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.news;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.news;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
\ No newline at end of file
diff --git a/news/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/news/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/news/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/news/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/news/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/news/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/news/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/news/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/news/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/news/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/news/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..a28140c
--- /dev/null
+++ b/news/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/news/ios/Runner.xcworkspace/contents.xcworkspacedata b/news/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/news/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/news/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/news/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/news/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/news/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/news/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/news/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/news/ios/Runner/AppDelegate.swift b/news/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..70693e4
--- /dev/null
+++ b/news/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..f7deb0c
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..94736df
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..ab0ae74
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..6c6516f
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..97bd7cb
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..bd9b434
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..5856eb1
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..ab0ae74
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..0ec7d36
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..58a312f
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..58a312f
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..d98f49a
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..6e4adf1
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..d5cdfef
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d392f78
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/news/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+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.
\ No newline at end of file
diff --git a/news/ios/Runner/Base.lproj/LaunchScreen.storyboard b/news/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/news/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/news/ios/Runner/Base.lproj/Main.storyboard b/news/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/news/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/news/ios/Runner/Info.plist b/news/ios/Runner/Info.plist
new file mode 100644
index 0000000..d8dda53
--- /dev/null
+++ b/news/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ news
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/news/ios/Runner/Runner-Bridging-Header.h b/news/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..308a2a5
--- /dev/null
+++ b/news/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/news/lib/api/news.dart b/news/lib/api/news.dart
new file mode 100644
index 0000000..25070c3
--- /dev/null
+++ b/news/lib/api/news.dart
@@ -0,0 +1,16 @@
+import 'dart:convert';
+import 'package:http/http.dart' as http;
+import 'package:news/model/article_model.dart';
+
+class News {
+ Future getNews(String category) async {
+ String url =
+ 'https://newsapi.org/v2/top-headlines?country=in&category=$category&apiKey=c75f43f6acbd4734aaa81a314a806b4b';
+ var response = await http.get(Uri.parse(url));
+ if (response.statusCode == 200) {
+ return Article.fromJson(jsonDecode(response.body));
+ } else {
+ throw Exception("Api Error");
+ }
+ }
+}
diff --git a/news/lib/main.dart b/news/lib/main.dart
new file mode 100644
index 0000000..8935da6
--- /dev/null
+++ b/news/lib/main.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/material.dart';
+import 'package:news/screen/profile/profile.dart';
+
+import 'screen/config/themes/themes.dart';
+import 'screen/home/home_page.dart';
+
+void main() {
+ runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ const MyApp({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Flutter Demo',
+ theme: themes(),
+ debugShowCheckedModeBanner: false,
+ routes: {
+ // DetailsNews.routeName: (ctx) => const DetailsNews(),
+ Profile.routeName: (ctx) => const Profile(),
+ },
+ home: const HomePage(),
+ );
+ }
+}
diff --git a/news/lib/model/article_model.dart b/news/lib/model/article_model.dart
new file mode 100644
index 0000000..c393375
--- /dev/null
+++ b/news/lib/model/article_model.dart
@@ -0,0 +1,100 @@
+// To parse this JSON data, do
+//
+// final article = articleFromJson(jsonString);
+
+import 'dart:convert';
+
+import 'package:news/screen/config/var/var.dart';
+
+Article articleFromJson(String str) => Article.fromJson(json.decode(str));
+
+String articleToJson(Article data) => json.encode(data.toJson());
+
+class Article {
+ Article({
+ required this.status,
+ required this.totalResults,
+ required this.articles,
+ });
+
+ final String status;
+ final int totalResults;
+ final List articles;
+
+ factory Article.fromJson(Map json) => Article(
+ status: json["status"],
+ totalResults: json["totalResults"],
+ articles: List.from(
+ json["articles"].map((x) => ArticleElement.fromJson(x))),
+ );
+
+ Map toJson() => {
+ "status": status,
+ "totalResults": totalResults,
+ "articles": List.from(articles.map((x) => x.toJson())),
+ };
+}
+
+class ArticleElement {
+ ArticleElement({
+ // required this.source,
+ required this.author,
+ required this.title,
+ required this.description,
+ required this.url,
+ required this.urlToImage,
+ required this.publishedAt,
+ required this.content,
+ });
+
+ // final Source source;
+ final String? author;
+ final String title;
+ final String description;
+ final String url;
+ final String? urlToImage;
+ final DateTime publishedAt;
+ final String? content;
+
+ factory ArticleElement.fromJson(Map json) => ArticleElement(
+ // source: Source.fromJson(json["source"]),
+ author: json["author"] ?? "AuthorName",
+ title: json["title"],
+ description: json["description"] ?? "Description",
+ url: json["url"],
+ urlToImage: json["urlToImage"] ?? placeHolderImage,
+ publishedAt: DateTime.parse(json["publishedAt"]),
+ content: json["content"] ?? "Content",
+ );
+
+ Map toJson() => {
+ // "source": source.toJson(),
+ "author": (author == null) ? null : author,
+ "title": title,
+ "description": description,
+ "url": url,
+ "urlToImage": urlToImage,
+ "publishedAt": publishedAt.toIso8601String(),
+ "content": (content == null) ? null : content,
+ };
+}
+
+// class Source {
+// Source({
+// required this.id,
+// required this.name,
+// });
+
+// final String id;
+// final String name;
+
+// factory Source.fromJson(Map json) => Source(
+// id: json["id"] == null ? null : json["id"],
+// name: json["name"],
+// );
+
+// Map toJson() => {
+// "id": id == null ? null : id,
+// "name": name,
+// };
+// }
diff --git a/news/lib/screen/config/themes/themes.dart b/news/lib/screen/config/themes/themes.dart
new file mode 100644
index 0000000..7c7bd44
--- /dev/null
+++ b/news/lib/screen/config/themes/themes.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+
+ThemeData themes() {
+ return ThemeData(
+ primarySwatch: Colors.blue,
+ textTheme: TextTheme(
+ bodyText1: TextStyle(
+ color: Colors.black45,
+ fontFamily: GoogleFonts.poppins().fontFamily),
+ ),
+ primaryTextTheme: TextTheme(
+ headline6: TextStyle(
+ color: Colors.blueGrey,
+ fontFamily: GoogleFonts.poppins().fontFamily),
+ headline4: TextStyle(
+ color: Colors.blueGrey,
+ fontFamily: GoogleFonts.poppins().fontFamily),
+ headline3: TextStyle(
+ color: Colors.blueGrey,
+ fontWeight: FontWeight.w700,
+ fontFamily: GoogleFonts.poppins().fontFamily),
+ bodyText1: TextStyle(
+ color: Colors.blueGrey,
+ fontFamily: GoogleFonts.poppins().fontFamily),
+ ));
+}
diff --git a/news/lib/screen/config/var/var.dart b/news/lib/screen/config/var/var.dart
new file mode 100644
index 0000000..5f03ce7
--- /dev/null
+++ b/news/lib/screen/config/var/var.dart
@@ -0,0 +1,6 @@
+const profileImage =
+ 'https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=387&q=80';
+
+const newsPaperImage =
+ 'https://images.unsplash.com/photo-1493238792000-8113da705763?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1170&q=80';
+const placeHolderImage = 'https://i.stack.imgur.com/y9DpT.jpg';
diff --git a/news/lib/screen/details/article_view.dart b/news/lib/screen/details/article_view.dart
new file mode 100644
index 0000000..9c5febb
--- /dev/null
+++ b/news/lib/screen/details/article_view.dart
@@ -0,0 +1,39 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+
+class ArticleView extends StatefulWidget {
+ final String url;
+ const ArticleView({Key? key, required this.url}) : super(key: key);
+
+ @override
+ _ArticleViewState createState() => _ArticleViewState();
+}
+
+class _ArticleViewState extends State {
+ @override
+ void initState() {
+ super.initState();
+ if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ elevation: 0,
+ toolbarHeight: 40,
+ backgroundColor: Colors.transparent,
+ foregroundColor: Colors.black87,
+ ),
+ body: SafeArea(
+ child: WebView(
+ gestureNavigationEnabled: true,
+ initialUrl: widget.url,
+ ),
+ ),
+ extendBodyBehindAppBar: true,
+ );
+ }
+}
diff --git a/news/lib/screen/details/detail_news.dart b/news/lib/screen/details/detail_news.dart
new file mode 100644
index 0000000..3d407c7
--- /dev/null
+++ b/news/lib/screen/details/detail_news.dart
@@ -0,0 +1,58 @@
+import 'package:flutter/material.dart';
+import 'package:news/screen/home/widgets/main_bar.dart';
+
+class DetailsNews extends StatelessWidget {
+ const DetailsNews({Key? key}) : super(key: key);
+
+ static const routeName = 'DetailsNews';
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ backgroundColor: Colors.transparent,
+ elevation: 0,
+ leading: IconButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ icon: const Icon(Icons.arrow_back),
+ color: Colors.black54,
+ ),
+ actions: const [
+ Padding(
+ padding: EdgeInsets.only(right: 20),
+ child: Icon(
+ Icons.share_rounded,
+ color: Colors.black54,
+ ),
+ )
+ ],
+ ),
+ body: SafeArea(
+ child: SingleChildScrollView(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const MainBar(),
+ const SizedBox(
+ height: 12,
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 8),
+ child: Text(
+ 'Excepteur sunt do qui tempor culpa fugiat fugiat voluptate laboris Lorem magna irure ea pariatur. Magna ea esse ipsum id ex consequat officia commodo voluptate ex sit ea minim deserunt. Veniam aute mollit fugiat ullamco laborum. Ex minim ad consequat aute Lorem commodo sit enim elit labore qui irure enim ipsum. Eu sint cillum excepteur exercitation est velit laboris eu.',
+ style: Theme.of(context)
+ .primaryTextTheme
+ .bodyText1!
+ .merge(const TextStyle(wordSpacing: 3))),
+ )
+ ],
+ ),
+ ),
+ )),
+ );
+ }
+}
diff --git a/news/lib/screen/home/home_page.dart b/news/lib/screen/home/home_page.dart
new file mode 100644
index 0000000..c9b420b
--- /dev/null
+++ b/news/lib/screen/home/home_page.dart
@@ -0,0 +1,75 @@
+import 'package:flutter/material.dart';
+import 'package:news/screen/home/widgets/custom_appbar.dart';
+
+import 'widgets/category_list.dart';
+import 'widgets/recent_news.dart';
+
+class HomePage extends StatefulWidget {
+ const HomePage({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _HomePageState();
+}
+
+class _HomePageState extends State {
+ String category = "General";
+ void updateCategory(newCategory) {
+ setState(() {
+ category = newCategory;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final screenWidth = MediaQuery.of(context).size.width;
+ return Scaffold(
+ appBar: const MyCustomAppBar(),
+ body: SafeArea(
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // CustomAppBar(),
+ // TitleBar(screenWidth: screenWidth),
+ Padding(
+ padding: const EdgeInsets.only(left: 20, right: 20, top: 10),
+ child: CategoryList(
+ onCategoryChanged: (String category) {
+ updateCategory(category);
+ },
+ currentCategory: category,
+ ),
+ ),
+ RecentNews(
+ screenWidth: screenWidth,
+ category: category,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// leading: Padding(
+// padding: const EdgeInsets.only(left: 12),
+// child: IconButton(
+// icon: const Icon(Icons.search),
+// onPressed: () {}, // omitting onPressed makes the button disabled
+// ),
+// ),
+// title: null, // 2
+// centerTitle: true,
+// actions: [
+// GestureDetector(
+// onTap: () {
+// Navigator.of(context).pushNamed(Profile.routeName);
+// },
+// child: const CircleAvatar(
+// radius: 21,
+// backgroundImage: NetworkImage(profileImage),
+// ),
+// ), // avatar added here
+// ], // 3
+// ),
diff --git a/news/lib/screen/home/home_page_old.dart b/news/lib/screen/home/home_page_old.dart
new file mode 100644
index 0000000..79828e3
--- /dev/null
+++ b/news/lib/screen/home/home_page_old.dart
@@ -0,0 +1,73 @@
+import 'package:flutter/material.dart';
+import 'package:news/screen/home/widgets/custom_appbar.dart';
+import 'widgets/category_list.dart';
+import 'widgets/recent_news.dart';
+
+class HomePage extends StatefulWidget {
+ const HomePage({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _HomePageState();
+}
+
+class _HomePageState extends State {
+ String? category = "General";
+ void updateCategory(newCategory) {
+ setState(() {
+ category = newCategory;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final screenWidth = MediaQuery.of(context).size.width;
+ return Scaffold(
+ appBar: const MyCustomAppBar(),
+ body: SafeArea(
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // CustomAppBar(),
+ // TitleBar(screenWidth: screenWidth),
+ Padding(
+ padding: const EdgeInsets.only(left: 20, right: 20, top: 10),
+ child: CategoryList(
+ onCategoryChanged: (String category) {
+ updateCategory(category);
+ }, currentCategory: '',
+ ),
+ ),
+ RecentNews(
+ screenWidth: screenWidth,
+ category: category ?? "General",
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// leading: Padding(
+// padding: const EdgeInsets.only(left: 12),
+// child: IconButton(
+// icon: const Icon(Icons.search),
+// onPressed: () {}, // omitting onPressed makes the button disabled
+// ),
+// ),
+// title: null, // 2
+// centerTitle: true,
+// actions: [
+// GestureDetector(
+// onTap: () {
+// Navigator.of(context).pushNamed(Profile.routeName);
+// },
+// child: const CircleAvatar(
+// radius: 21,
+// backgroundImage: NetworkImage(profileImage),
+// ),
+// ), // avatar added here
+// ], // 3
+// ),
diff --git a/news/lib/screen/home/widgets/category_list.dart b/news/lib/screen/home/widgets/category_list.dart
new file mode 100644
index 0000000..d94890a
--- /dev/null
+++ b/news/lib/screen/home/widgets/category_list.dart
@@ -0,0 +1,94 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+
+typedef IntCallBack = void Function(String category);
+
+class CategoryList extends StatefulWidget {
+ final IntCallBack onCategoryChanged;
+ final String currentCategory;
+ const CategoryList({
+ Key? key,
+ required this.onCategoryChanged,
+ required this.currentCategory
+ }) : super(key: key);
+
+ @override
+ State createState() => _CategoryListState();
+}
+
+class _CategoryListState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final List categoryList = [
+ 'General',
+ 'Business',
+ 'Entertainment',
+ 'Health',
+ 'Sports',
+ 'Technology'
+ ];
+ return SizedBox(
+ height: 50,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: categoryList.map((type) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 15,
+ ),
+ child: GestureDetector(
+ onTap: () {
+ widget.onCategoryChanged(type);
+ },
+ child: Text(
+ type,
+ style: GoogleFonts.poppins(
+ color: (widget.currentCategory==type)?Colors.black87 : Colors.black54, fontWeight: FontWeight.w500),
+ ),
+ ),
+ );
+ }).toList()),
+ );
+ }
+}
+
+ // Padding(
+ // padding: const EdgeInsets.only(left: 15.0),
+ // child: Text(
+ // 'All',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Information',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Media',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Magazine',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Business',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
\ No newline at end of file
diff --git a/news/lib/screen/home/widgets/category_list_old.dart b/news/lib/screen/home/widgets/category_list_old.dart
new file mode 100644
index 0000000..bd49b7c
--- /dev/null
+++ b/news/lib/screen/home/widgets/category_list_old.dart
@@ -0,0 +1,92 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+
+typedef IntCallBack = void Function(String category);
+
+class CategoryList extends StatefulWidget {
+ final IntCallBack onCategoryChanged;
+ const CategoryList({
+ Key? key,
+ required this.onCategoryChanged,
+ }) : super(key: key);
+
+ @override
+ State createState() => _CategoryListState();
+}
+
+class _CategoryListState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final List categoryList = [
+ 'General',
+ 'Business',
+ 'Entertainment',
+ 'Health',
+ 'Sports',
+ 'Technology'
+ ];
+ return SizedBox(
+ height: 50,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: categoryList.map((type) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 15,
+ ),
+ child: GestureDetector(
+ onTap: () {
+ widget.onCategoryChanged(type);
+ },
+ child: Text(
+ type,
+ style: GoogleFonts.poppins(
+ color: Colors.black54, fontWeight: FontWeight.w500),
+ ),
+ ),
+ );
+ }).toList()),
+ );
+ }
+}
+
+ // Padding(
+ // padding: const EdgeInsets.only(left: 15.0),
+ // child: Text(
+ // 'All',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Information',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Media',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Magazine',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(left: 25.0),
+ // child: Text(
+ // 'Business',
+ // style: GoogleFonts.poppins(
+ // color: Colors.black45, fontWeight: FontWeight.w500),
+ // ),
+ // ),
\ No newline at end of file
diff --git a/news/lib/screen/home/widgets/custom_app_bar.dart b/news/lib/screen/home/widgets/custom_app_bar.dart
new file mode 100644
index 0000000..3aa34ed
--- /dev/null
+++ b/news/lib/screen/home/widgets/custom_app_bar.dart
@@ -0,0 +1,55 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+import '../../profile/profile.dart';
+import '../../config/var/var.dart';
+
+class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
+ const CustomAppBar({
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.only(top: 5, bottom: 5, left: 25, right: 25),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ GestureDetector(
+ onTap: () {
+ Navigator.of(context).pushNamed(Profile.routeName);
+ },
+ child: const CircleAvatar(
+ radius: 21,
+ backgroundImage: NetworkImage(profileImage),
+ ),
+ ),
+ Row(
+ children: [
+ Text('Alpha ',
+ style: TextStyle(
+ color: Colors.black45,
+ fontFamily: GoogleFonts.poppins().fontFamily,
+ fontSize: 25,
+ fontWeight: FontWeight.w600,
+ )),
+ Text('News',
+ style: TextStyle(
+ color: Colors.black45,
+ fontFamily: GoogleFonts.poppins().fontFamily,
+ fontSize: 25,
+ // fontWeight: FontWeight.w500,
+ )),
+ ],
+ ),
+ const Icon(Icons.search, size: 30)
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Size get preferredSize => const Size.fromHeight(60);
+}
diff --git a/news/lib/screen/home/widgets/custom_appbar.dart b/news/lib/screen/home/widgets/custom_appbar.dart
new file mode 100644
index 0000000..d603082
--- /dev/null
+++ b/news/lib/screen/home/widgets/custom_appbar.dart
@@ -0,0 +1,61 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:news/screen/config/var/var.dart';
+import 'package:news/screen/profile/profile.dart';
+
+class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget {
+ const MyCustomAppBar({Key? key}) : super(key: key);
+
+ static double height = 66;
+
+ @override
+ Widget build(BuildContext context) {
+ AppBar appBar = AppBar(
+ titleSpacing: 50,
+ backgroundColor: Colors.transparent,
+ foregroundColor: Colors.black87,
+ elevation: 0,
+ centerTitle: true,
+ title: Center(
+ child: Row(
+ children: [
+ Text('Alpha ',
+ style: TextStyle(
+ color: Colors.black87,
+ fontFamily: GoogleFonts.poppins().fontFamily,
+ fontSize: 25,
+ fontWeight: FontWeight.w600,
+ )),
+ Text('News',
+ style: TextStyle(
+ color: Colors.black87,
+ fontFamily: GoogleFonts.poppins().fontFamily,
+ fontSize: 28,
+ // fontWeight: FontWeight.w500,
+ )),
+ ],
+ ),
+ ),
+ leading: GestureDetector(
+ onTap: () {
+ Navigator.of(context).pushNamed(Profile.routeName);
+ },
+ child: const CircleAvatar(
+ radius: 21,
+ backgroundImage: NetworkImage(profileImage),
+ ),
+ ),
+ actions: const [Icon(Icons.search, size: 30)],
+ );
+
+ // height == appBar.preferredSize.height;
+ // print("\nHeight " + height.toString() + "\n");
+ return Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 10, left: 13, right: 13),
+ child: appBar,
+ );
+ }
+
+ @override
+ Size get preferredSize => Size.fromHeight(height);
+}
diff --git a/news/lib/screen/home/widgets/custom_tile.dart b/news/lib/screen/home/widgets/custom_tile.dart
new file mode 100644
index 0000000..c231fb6
--- /dev/null
+++ b/news/lib/screen/home/widgets/custom_tile.dart
@@ -0,0 +1,154 @@
+import 'package:flutter/material.dart';
+import 'package:news/screen/details/article_view.dart';
+
+class CustomTile extends StatefulWidget {
+ const CustomTile({
+ Key? key,
+ required this.screenWidth,
+ required this.title,
+ required this.description,
+ required this.url,
+ required this.urlToImage,
+ }) : super(key: key);
+
+ final double screenWidth;
+ // final String author;
+ final String title;
+ final String description;
+ final String url;
+ final String urlToImage;
+
+ @override
+ State createState() => _CustomTileState();
+}
+
+class _CustomTileState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => ArticleView(url: widget.url)));
+ },
+ child: Container(
+ padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
+ decoration: const BoxDecoration(
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black12,
+ spreadRadius: 2,
+ blurRadius: 30,
+ // offset: Offset(0, 5),
+ )
+ ],
+ ),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(18),
+ child: Container(
+ color: Colors.white,
+ padding: const EdgeInsets.only(bottom: 15),
+ child: Column(
+ children: [
+ ClipRRect(
+ borderRadius: BorderRadius.circular(18),
+ child: Image.network(
+ widget.urlToImage,
+ fit: BoxFit.cover,
+ ),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ ListTile(
+ title: Text(widget.title),
+ subtitle: Text(widget.description),
+ ),
+ ],
+ ),
+ /* Row(
+ children: [
+ SizedBox(
+ height: 80,
+ width: 100,
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(8),
+ child: Image.network(
+ newsPaperImage,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(
+ width: screenWidth * 0.5,
+ child: Text(
+ "This is a little way to create flutter",
+ style: Theme.of(context).primaryTextTheme.bodyText1!.merge(
+ const TextStyle(
+ fontWeight: FontWeight.w700,
+ wordSpacing: 2.5,
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: const [
+ IconText(
+ title: 'Jan 10, 2021',
+ iconData: Icons.calendar_today,
+ ),
+ SizedBox(width: 20),
+ IconText(
+ title: '10 min Read',
+ iconData: Icons.lock_clock,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ), */
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// class IconText extends StatelessWidget {
+// const IconText({Key? key, required this.iconData, required this.title})
+// : super(key: key);
+
+// final IconData iconData;
+// final String title;
+
+// @override
+// Widget build(BuildContext context) {
+// return Row(
+// children: [
+// Icon(
+// iconData,
+// size: 17,
+// ),
+// const SizedBox(
+// width: 6,
+// ),
+// Text(
+// title,
+// style: Theme.of(context).textTheme.bodyText1,
+// ),
+// ],
+// );
+// }
+// }
diff --git a/news/lib/screen/home/widgets/main_bar.dart b/news/lib/screen/home/widgets/main_bar.dart
new file mode 100644
index 0000000..6b03f15
--- /dev/null
+++ b/news/lib/screen/home/widgets/main_bar.dart
@@ -0,0 +1,57 @@
+import 'package:flutter/material.dart';
+import '../../config/var/var.dart';
+
+class MainBar extends StatelessWidget {
+ const MainBar({
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ ClipRRect(
+ borderRadius: BorderRadius.circular(18),
+ child: Image.network(
+ newsPaperImage,
+ fit: BoxFit.cover,
+ ),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Container(
+ padding: const EdgeInsets.only(left: 15),
+ child: Text(
+ 'Est voluptate ex amet pariatur mollit fugiat consectetur anim.',
+ style: Theme.of(context).primaryTextTheme.headline6,
+ ),
+ ),
+ const SizedBox(
+ height: 3,
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ const CircleAvatar(
+ radius: 20,
+ backgroundImage: NetworkImage(profileImage),
+ ),
+ const SizedBox(width: 10),
+ Text('Nahsor Lihda',
+ style: Theme.of(context).textTheme.bodyText1),
+ ],
+ ),
+ Text('23 Sep, 2021',
+ style: Theme.of(context).textTheme.bodyText1),
+ ],
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/news/lib/screen/home/widgets/recent_news.dart b/news/lib/screen/home/widgets/recent_news.dart
new file mode 100644
index 0000000..ff82ce3
--- /dev/null
+++ b/news/lib/screen/home/widgets/recent_news.dart
@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:news/api/news.dart';
+import 'package:news/model/article_model.dart';
+import 'package:news/screen/config/var/var.dart';
+
+import 'custom_tile.dart';
+
+class RecentNews extends StatefulWidget {
+ const RecentNews({
+ Key? key,
+ required this.screenWidth,
+ required this.category,
+ }) : super(key: key);
+
+ final double screenWidth;
+ final String category;
+
+ @override
+ State createState() => _RecentNewsState();
+}
+
+class _RecentNewsState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: News().getNews(widget.category),
+ builder: (context, snapshot) {
+ if (snapshot.hasData) {
+ return ListView.builder(
+ shrinkWrap: true,
+ scrollDirection: Axis.vertical,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: snapshot.data!.articles.length,
+ itemBuilder: (BuildContext context, int index) {
+ var data = snapshot.data!.articles[index];
+ return CustomTile(
+ screenWidth: widget.screenWidth,
+ title: data.title,
+ description: data.description,
+ url: data.url,
+ urlToImage: data.urlToImage ?? placeHolderImage,
+ );
+ },
+ );
+ } else if (snapshot.hasError) {
+ return Text("snap err " + snapshot.error.toString());
+ } else {
+ return const Center(child: Center(child: CircularProgressIndicator()));
+ }
+ });
+ }
+}
diff --git a/news/lib/screen/home/widgets/title_bar.dart b/news/lib/screen/home/widgets/title_bar.dart
new file mode 100644
index 0000000..64a3abe
--- /dev/null
+++ b/news/lib/screen/home/widgets/title_bar.dart
@@ -0,0 +1,54 @@
+/* import 'package:flutter/material.dart';
+
+class TitleBar extends StatelessWidget {
+ const TitleBar({
+ Key? key,
+ required this.screenWidth,
+ }) : super(key: key);
+
+ final double screenWidth;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Text(
+ // 'Breaking News',
+ // style: Theme.of(context).primaryTextTheme.headline3,
+ // ),
+ // const SizedBox(
+ // height: 10,
+ // ),
+ // GestureDetector(
+ // onTap: () {
+ // Navigator.of(context).pushNamed(DetailsNews.routeName);
+ // },
+ // child: Container(
+ // decoration: const BoxDecoration(
+ // boxShadow: [
+ // BoxShadow(
+ // color: Colors.black12,
+ // spreadRadius: 2,
+ // blurRadius: 30,
+ // // offset: Offset(0, 5),
+ // )
+ // ],
+ // ),
+ // child: ClipRRect(
+ // borderRadius: BorderRadius.circular(18),
+ // child: Container(
+ // color: Colors.white,
+ // child: const MainBar(),
+ // ),
+ // ),
+ // ),
+ // ),
+ ],
+ ),
+ );
+ }
+}
+ */
\ No newline at end of file
diff --git a/news/lib/screen/profile/profile.dart b/news/lib/screen/profile/profile.dart
new file mode 100644
index 0000000..f68298d
--- /dev/null
+++ b/news/lib/screen/profile/profile.dart
@@ -0,0 +1,31 @@
+import 'package:flutter/material.dart';
+import 'widgets/profile_details.dart';
+
+class Profile extends StatelessWidget {
+ const Profile({Key? key}) : super(key: key);
+
+ static const routeName = 'Profile';
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ icon: const Icon(Icons.arrow_back_rounded),
+ color: Colors.black54,
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ elevation: 0,
+ backgroundColor: Colors.transparent,
+ ),
+ body: Container(
+ height: 600,
+ width: double.infinity,
+ padding: const EdgeInsets.only(left: 22, right: 22, bottom: 22),
+ child: const ProfileDetails(),
+ ),
+ );
+ }
+}
diff --git a/news/lib/screen/profile/widgets/data.dart b/news/lib/screen/profile/widgets/data.dart
new file mode 100644
index 0000000..2eb55c2
--- /dev/null
+++ b/news/lib/screen/profile/widgets/data.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+
+// ignore: must_be_immutable
+class Data extends StatelessWidget {
+ Data({Key? key, required this.label, required this.text}) : super(key: key);
+
+ String label;
+ String text;
+
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ children: [
+ Text(
+ label,
+ style: GoogleFonts.poppins(),
+ ),
+ const Spacer(),
+ Text(
+ text,
+ style: GoogleFonts.poppins(),
+ ),
+ ],
+ );
+ }
+}
diff --git a/news/lib/screen/profile/widgets/profile_details.dart b/news/lib/screen/profile/widgets/profile_details.dart
new file mode 100644
index 0000000..71e0444
--- /dev/null
+++ b/news/lib/screen/profile/widgets/profile_details.dart
@@ -0,0 +1,87 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+import '../../config/var/var.dart';
+import 'data.dart';
+
+class ProfileDetails extends StatelessWidget {
+ const ProfileDetails({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ Align(
+ child: Text(
+ "Edit Profile",
+ style: GoogleFonts.poppins(
+ textStyle:
+ const TextStyle(fontSize: 26, fontWeight: FontWeight.w700),
+ ),
+ ),
+ alignment: Alignment.topLeft,
+ ),
+ const SizedBox(height: 10),
+ Container(
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ shape: BoxShape.circle,
+ boxShadow: [
+ BoxShadow(
+ color: Colors.grey,
+ spreadRadius: 3,
+ blurRadius: 15,
+ offset: Offset(0, 5),
+ )
+ ],
+ ),
+ child: const CircleAvatar(
+ backgroundImage: NetworkImage(profileImage),
+ radius: 70,
+ ),
+ ),
+ const Spacer(
+ flex: 2,
+ ),
+ // UserName
+ Data(label: "Username", text: "Nahsor Lihda"),
+ const Spacer(),
+ const Divider(
+ color: Colors.black12,
+ thickness: 2,
+ ),
+ const Spacer(),
+ // Email
+ Data(label: "Email", text: "abc@gmail.com"),
+ const Spacer(),
+ const Divider(
+ color: Colors.black12,
+ thickness: 2,
+ ),
+ const Spacer(),
+ // Phone
+ Data(label: "Phone", text: "+44-20-7224-3688"),
+ const Spacer(),
+ const Divider(
+ color: Colors.black12,
+ thickness: 2,
+ ),
+ const Spacer(),
+ // DOB
+ Data(label: "Date of Birth", text: "20/01/2001"),
+ const Spacer(),
+ const Divider(
+ color: Colors.black12,
+ thickness: 2,
+ ),
+ const Spacer(),
+ // Address
+ Data(label: "Address", text: "221B Baker Street, London"),
+ const Spacer(),
+ const Divider(
+ color: Colors.black12,
+ thickness: 2,
+ ),
+ ],
+ );
+ }
+}
diff --git a/news/pubspec.lock b/news/pubspec.lock
new file mode 100644
index 0000000..88676eb
--- /dev/null
+++ b/news/pubspec.lock
@@ -0,0 +1,357 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.6"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.8.1"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ charcode:
+ dependency: transitive
+ description:
+ name: charcode
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.1"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.15.0"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.1"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.3"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.2"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "6.1.2"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_launcher_icons:
+ dependency: "direct dev"
+ description:
+ name: flutter_launcher_icons
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.9.2"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.4"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ google_fonts:
+ dependency: "direct main"
+ description:
+ name: google_fonts
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ http:
+ dependency: "direct main"
+ description:
+ name: http
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.13.4"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.0"
+ image:
+ dependency: transitive
+ description:
+ name: image
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.8"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.12.10"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.8.0"
+ path_provider:
+ dependency: transitive
+ description:
+ name: path_provider
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.5"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ path_provider_macos:
+ dependency: transitive
+ description:
+ name: path_provider_macos
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.2"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.3"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.4.0"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.2"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.2.3"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.8.1"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.10.0"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.2"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.0"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ webview_flutter:
+ dependency: "direct main"
+ description:
+ name: webview_flutter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.2"
+ webview_flutter_android:
+ dependency: transitive
+ description:
+ name: webview_flutter_android
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.0"
+ webview_flutter_platform_interface:
+ dependency: transitive
+ description:
+ name: webview_flutter_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.0"
+ webview_flutter_wkwebview:
+ dependency: transitive
+ description:
+ name: webview_flutter_wkwebview
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.0"
+ win32:
+ dependency: transitive
+ description:
+ name: win32
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.9"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.3.1"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.0"
+sdks:
+ dart: ">=2.14.0 <3.0.0"
+ flutter: ">=2.5.0"
diff --git a/news/pubspec.yaml b/news/pubspec.yaml
new file mode 100644
index 0000000..79c600d
--- /dev/null
+++ b/news/pubspec.yaml
@@ -0,0 +1,98 @@
+name: news
+description: A new Flutter project.
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `flutter pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+# The following defines the version and build number for your application.
+# A version number is three numbers separated by dots, like 1.2.43
+# followed by an optional build number separated by a +.
+# Both the version and the builder number may be overridden in flutter
+# build by specifying --build-name and --build-number, respectively.
+# In Android, build-name is used as versionName while build-number used as versionCode.
+# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
+# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
+# Read more about iOS versioning at
+# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.12.0 <3.0.0"
+
+# Dependencies specify other packages that your package needs in order to work.
+# To automatically upgrade your package dependencies to the latest versions
+# consider running `flutter pub upgrade --major-versions`. Alternatively,
+# dependencies can be manually updated by changing the version numbers below to
+# the latest version available on pub.dev. To see which dependencies have newer
+# versions available, run `flutter pub outdated`.
+dependencies:
+ flutter:
+ sdk: flutter
+
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.2
+ google_fonts: ^2.1.0
+ http: ^0.13.4
+ webview_flutter: ^2.1.2
+
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ # The "flutter_lints" package below contains a set of recommended lints to
+ # encourage good coding practices. The lint set provided by the package is
+ # activated in the `analysis_options.yaml` file located at the root of your
+ # package. See that file for information about deactivating specific lint
+ # rules and activating additional ones.
+ flutter_lints: ^1.0.0
+ flutter_launcher_icons: ^0.9.2
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ assets:
+ - assets/
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware.
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
+flutter_icons:
+ android: "launcher_icon"
+ ios: true
+ image_path: "assets/icon.png"
\ No newline at end of file
diff --git a/news/test/widget_test.dart b/news/test/widget_test.dart
new file mode 100644
index 0000000..5c68241
--- /dev/null
+++ b/news/test/widget_test.dart
@@ -0,0 +1,30 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:news/main.dart';
+
+void main() {
+ testWidgets('Counter increments smoke test', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(const MyApp());
+
+ // Verify that our counter starts at 0.
+ expect(find.text('0'), findsOneWidget);
+ expect(find.text('1'), findsNothing);
+
+ // Tap the '+' icon and trigger a frame.
+ await tester.tap(find.byIcon(Icons.add));
+ await tester.pump();
+
+ // Verify that our counter has incremented.
+ expect(find.text('0'), findsNothing);
+ expect(find.text('1'), findsOneWidget);
+ });
+}
diff --git a/news/web/favicon.png b/news/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
Binary files /dev/null and b/news/web/favicon.png differ
diff --git a/news/web/icons/Icon-192.png b/news/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
Binary files /dev/null and b/news/web/icons/Icon-192.png differ
diff --git a/news/web/icons/Icon-512.png b/news/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
Binary files /dev/null and b/news/web/icons/Icon-512.png differ
diff --git a/news/web/icons/Icon-maskable-192.png b/news/web/icons/Icon-maskable-192.png
new file mode 100644
index 0000000..eb9b4d7
Binary files /dev/null and b/news/web/icons/Icon-maskable-192.png differ
diff --git a/news/web/icons/Icon-maskable-512.png b/news/web/icons/Icon-maskable-512.png
new file mode 100644
index 0000000..d69c566
Binary files /dev/null and b/news/web/icons/Icon-maskable-512.png differ
diff --git a/news/web/index.html b/news/web/index.html
new file mode 100644
index 0000000..b0c7931
--- /dev/null
+++ b/news/web/index.html
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ news
+
+
+
+
+
+
+
diff --git a/news/web/manifest.json b/news/web/manifest.json
new file mode 100644
index 0000000..1fe1f47
--- /dev/null
+++ b/news/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "news",
+ "short_name": "news",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/news/windows/.gitignore b/news/windows/.gitignore
new file mode 100644
index 0000000..d492d0d
--- /dev/null
+++ b/news/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/news/windows/CMakeLists.txt b/news/windows/CMakeLists.txt
new file mode 100644
index 0000000..fe14e69
--- /dev/null
+++ b/news/windows/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.15)
+project(news LANGUAGES CXX)
+
+set(BINARY_NAME "news")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/news/windows/flutter/CMakeLists.txt b/news/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000..b02c548
--- /dev/null
+++ b/news/windows/flutter/CMakeLists.txt
@@ -0,0 +1,103 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/news/windows/flutter/generated_plugin_registrant.cc b/news/windows/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000..8b6d468
--- /dev/null
+++ b/news/windows/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,11 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+
+void RegisterPlugins(flutter::PluginRegistry* registry) {
+}
diff --git a/news/windows/flutter/generated_plugin_registrant.h b/news/windows/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000..dc139d8
--- /dev/null
+++ b/news/windows/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void RegisterPlugins(flutter::PluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/news/windows/flutter/generated_plugins.cmake b/news/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..4d10c25
--- /dev/null
+++ b/news/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/news/windows/runner/CMakeLists.txt b/news/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000..0b899a0
--- /dev/null
+++ b/news/windows/runner/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.15)
+project(runner LANGUAGES CXX)
+
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+apply_standard_settings(${BINARY_NAME})
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/news/windows/runner/Runner.rc b/news/windows/runner/Runner.rc
new file mode 100644
index 0000000..c828e52
--- /dev/null
+++ b/news/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#ifdef FLUTTER_BUILD_NUMBER
+#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#else
+#define VERSION_AS_NUMBER 1,0,0
+#endif
+
+#ifdef FLUTTER_BUILD_NAME
+#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "com.example" "\0"
+ VALUE "FileDescription", "A new Flutter project." "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "news" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0"
+ VALUE "OriginalFilename", "news.exe" "\0"
+ VALUE "ProductName", "news" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/news/windows/runner/flutter_window.cpp b/news/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000..b43b909
--- /dev/null
+++ b/news/windows/runner/flutter_window.cpp
@@ -0,0 +1,61 @@
+#include "flutter_window.h"
+
+#include
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/news/windows/runner/flutter_window.h b/news/windows/runner/flutter_window.h
new file mode 100644
index 0000000..6da0652
--- /dev/null
+++ b/news/windows/runner/flutter_window.h
@@ -0,0 +1,33 @@
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include
+#include
+
+#include
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/news/windows/runner/main.cpp b/news/windows/runner/main.cpp
new file mode 100644
index 0000000..56c5038
--- /dev/null
+++ b/news/windows/runner/main.cpp
@@ -0,0 +1,43 @@
+#include
+#include
+#include
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector command_line_arguments =
+ GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"news", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/news/windows/runner/resource.h b/news/windows/runner/resource.h
new file mode 100644
index 0000000..66a65d1
--- /dev/null
+++ b/news/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/news/windows/runner/resources/app_icon.ico b/news/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000..c04e20c
Binary files /dev/null and b/news/windows/runner/resources/app_icon.ico differ
diff --git a/news/windows/runner/runner.exe.manifest b/news/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000..c977c4a
--- /dev/null
+++ b/news/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+
+
+
+
+ PerMonitorV2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/news/windows/runner/utils.cpp b/news/windows/runner/utils.cpp
new file mode 100644
index 0000000..d19bdbb
--- /dev/null
+++ b/news/windows/runner/utils.cpp
@@ -0,0 +1,64 @@
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE *unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector();
+ }
+
+ std::vector command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ -1, nullptr, 0, nullptr, nullptr);
+ if (target_length == 0) {
+ return std::string();
+ }
+ std::string utf8_string;
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/news/windows/runner/utils.h b/news/windows/runner/utils.h
new file mode 100644
index 0000000..3879d54
--- /dev/null
+++ b/news/windows/runner/utils.h
@@ -0,0 +1,19 @@
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include
+#include
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector,
+// encoded in UTF-8. Returns an empty std::vector on failure.
+std::vector GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/news/windows/runner/win32_window.cpp b/news/windows/runner/win32_window.cpp
new file mode 100644
index 0000000..c10f08d
--- /dev/null
+++ b/news/windows/runner/win32_window.cpp
@@ -0,0 +1,245 @@
+#include "win32_window.h"
+
+#include
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() {
+ ++g_active_window_count;
+}
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title,
+ const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast(origin.x),
+ static_cast(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast(window_struct->lpCreateParams));
+
+ auto that = static_cast(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() {
+ return window_handle_;
+}
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
diff --git a/news/windows/runner/win32_window.h b/news/windows/runner/win32_window.h
new file mode 100644
index 0000000..17ba431
--- /dev/null
+++ b/news/windows/runner/win32_window.h
@@ -0,0 +1,98 @@
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include
+
+#include
+#include
+#include
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates and shows a win32 window with |title| and position and size using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size to will treat the width height passed in to this function
+ // as logical pixels and scale to appropriate for the default monitor. Returns
+ // true if the window was created successfully.
+ bool CreateAndShow(const std::wstring& title,
+ const Point& origin,
+ const Size& size);
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responsponds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_