├── plus.png
├── kotlin.png
├── sample.gif
├── flutter.png
├── assets
├── loader.gif
└── flutter-mark-square-64.png
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── values
│ │ │ │ │ │ ├── colors.xml
│ │ │ │ │ │ ├── strings.xml
│ │ │ │ │ │ └── dimens.xml
│ │ │ │ │ ├── color
│ │ │ │ │ │ └── fab_ripple_color.xml
│ │ │ │ │ ├── drawable
│ │ │ │ │ │ └── ic_add_black_24dp.xml
│ │ │ │ │ └── layout
│ │ │ │ │ │ └── flutter_view_layout.xml
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── color
│ │ │ │ │ └── fab_ripple_color.xml
│ │ │ │ ├── drawable
│ │ │ │ │ ├── ic_add_black_24dp.xml
│ │ │ │ │ └── launch_background.xml
│ │ │ │ └── layout
│ │ │ │ │ └── flutter_view_layout.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── flutlin
│ │ │ │ │ ├── apiConfig
│ │ │ │ │ ├── ApiError.kt
│ │ │ │ │ ├── model
│ │ │ │ │ │ ├── Picture.kt
│ │ │ │ │ │ └── User.kt
│ │ │ │ │ ├── response
│ │ │ │ │ │ └── UserResponse.kt
│ │ │ │ │ ├── WebApi.kt
│ │ │ │ │ ├── ApiParam.kt
│ │ │ │ │ ├── ErrorUtil.kt
│ │ │ │ │ └── WebApiClient.kt
│ │ │ │ │ └── activity
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── build.gradle
├── .metadata
├── LICENSE
├── test
└── widget_test.dart
├── .gitignore
├── README.md
├── lib
└── main.dart
├── pubspec.yaml
└── pubspec.lock
/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/plus.png
--------------------------------------------------------------------------------
/kotlin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/kotlin.png
--------------------------------------------------------------------------------
/sample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/sample.gif
--------------------------------------------------------------------------------
/flutter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/flutter.png
--------------------------------------------------------------------------------
/assets/loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/assets/loader.gif
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/assets/flutter-mark-square-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/assets/flutter-mark-square-64.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/Flutlin/HEAD/android/app/src/main/res/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - #9E9E9E
4 | - #FFFFFF
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - #9E9E9E
4 | - #FFFFFF
5 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/ApiError.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig
2 |
3 | data class ApiError(val message: String, val status: Int, val otp: Long, val token: String) {
4 | constructor() : this("", 0, 0, "")
5 | }
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/model/Picture.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | class Picture {
6 | @SerializedName("large")
7 | var image: String = ""
8 | }
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/model/User.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | class User {
6 | @SerializedName("picture")
7 | var picture: Picture? = null
8 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Flutlin
4 | Flutter Application
5 | Android
6 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
6 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/response/UserResponse.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig.response
2 |
3 | import com.flutlin.apiConfig.model.User
4 | import com.google.gson.annotations.SerializedName
5 |
6 | open class UserResponse {
7 | @SerializedName("results")
8 | var users: List? = null
9 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 6dp
4 | 12dp
5 | 17sp
6 | 30sp
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 6dp
4 | 12dp
5 | 17sp
6 | 30sp
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Flutlin
4 | Flutter Application
5 | Flutter button tapped 0 times.
6 | Android
7 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: e5b1ed7a7f7b85c1877e09a9495681f719be5578
8 | channel: beta
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/android/app/src/main/res/color/fab_ripple_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/res/color/fab_ripple_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_add_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/WebApi.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig
2 |
3 | import com.flutlin.apiConfig.response.UserResponse
4 | import io.reactivex.Observable
5 | import retrofit2.Response
6 | import retrofit2.http.GET
7 |
8 | interface WebApi {
9 | /* Random user */
10 | @GET("/api/?inc=picture&noinfo&results=50")
11 | fun randomUserApi(): Observable>
12 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/res/drawable/ic_add_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/flutter_view_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/res/layout/flutter_view_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/ApiParam.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig
2 |
3 | import java.util.*
4 |
5 | object ApiParam {
6 | var BASE_URL = "https://randomuser.me"
7 |
8 | val HTTP_SUCCESS_STATUS = 200
9 | val HTTP_VALIDATION_ERROR = 400
10 | val HTTP_UNAUTHORIZED_USER = 401
11 | val HTTP_UNVERIFIED_USER = 402
12 | val HTTP_DEACTIVED_USER = 403
13 | val HTTP_DELETED_USER = 405
14 | val HTTP_MAINTAINANCE_MODE = 503
15 |
16 | val META_STATUS_SUCCESS = 0
17 | val META_STATUS_FAIL = 1
18 | val META_STATUS_UNVERIFIED = 4
19 | val META_STATUS_CHANGE_PASSOWRD = 10
20 |
21 | fun getParam(): HashMap {
22 | return HashMap()
23 | }
24 |
25 | // Check App Version Api
26 | object AppVersion {
27 | const val API_TAG = "force-update"
28 | }
29 |
30 | // Country Api
31 | object Country {
32 | const val API_TAG = "country"
33 | }
34 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 MI Flutter / Flutter Libs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:flutlin/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/ErrorUtil.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig
2 |
3 | import com.google.gson.GsonBuilder
4 | import retrofit2.Response
5 | import java.io.IOException
6 |
7 | object ErrorUtil {
8 | val NO_INTERNET = "No internet connection."
9 | val SERVER_NOT_RESPONDING = "Server not responding."
10 | val SERVER_TIMEOUT = "Server timeout error."
11 | val SOMETHING_WENT_WRONG = "Something has gone wrong."
12 |
13 | fun parseError(response: Response<*>?): ApiError {
14 | var error: ApiError? = null
15 | try {
16 | response?.errorBody()?.let { errorBody ->
17 | errorBody.string().let { errorString ->
18 | val gson = GsonBuilder().create()
19 | error = gson.fromJson(errorString, ApiError::class.java)
20 | }
21 | }
22 | } catch (e: IOException) {
23 | e.printStackTrace()
24 | }
25 |
26 | return error ?: ApiError()
27 | }
28 |
29 | fun error(response: Response<*>): String {
30 | val error: ApiError? = parseError(response)
31 | return error?.message ?: ""
32 | }
33 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # Visual Studio Code related
19 | .vscode/
20 |
21 | # Flutter/Dart/Pub related
22 | **/doc/api/
23 | .dart_tool/
24 | .flutter-plugins
25 | .packages
26 | .pub-cache/
27 | .pub/
28 | /build/
29 |
30 | # Android related
31 | **/android/**/gradle-wrapper.jar
32 | **/android/.gradle
33 | **/android/captures/
34 | **/android/gradlew
35 | **/android/gradlew.bat
36 | **/android/local.properties
37 | **/android/**/GeneratedPluginRegistrant.java
38 |
39 | # iOS/XCode related
40 | **/ios/**/*.mode1v3
41 | **/ios/**/*.mode2v3
42 | **/ios/**/*.moved-aside
43 | **/ios/**/*.pbxuser
44 | **/ios/**/*.perspectivev3
45 | **/ios/**/*sync/
46 | **/ios/**/.sconsign.dblite
47 | **/ios/**/.tags*
48 | **/ios/**/.vagrant/
49 | **/ios/**/DerivedData/
50 | **/ios/**/Icon?
51 | **/ios/**/Pods/
52 | **/ios/**/.symlinks/
53 | **/ios/**/profile
54 | **/ios/**/xcuserdata
55 | **/ios/.generated/
56 | **/ios/Flutter/App.framework
57 | **/ios/Flutter/Flutter.framework
58 | **/ios/Flutter/Generated.xcconfig
59 | **/ios/Flutter/app.flx
60 | **/ios/Flutter/app.zip
61 | **/ios/Flutter/flutter_assets/
62 | **/ios/ServiceDefinitions.json
63 | **/ios/Runner/GeneratedPluginRegistrant.*
64 |
65 | # Exceptions to above rules.
66 | !**/ios/**/default.mode1v3
67 | !**/ios/**/default.mode2v3
68 | !**/ios/**/default.pbxuser
69 | !**/ios/**/default.perspectivev3
70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
71 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
11 |
12 |
16 |
24 |
28 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/apiConfig/WebApiClient.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.apiConfig
2 |
3 | import com.flutlin.BuildConfig
4 | import com.flutlin.apiConfig.ApiParam.BASE_URL
5 | import okhttp3.OkHttpClient
6 | import okhttp3.logging.HttpLoggingInterceptor
7 | import retrofit2.Retrofit
8 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
9 | import retrofit2.converter.gson.GsonConverterFactory
10 | import java.util.concurrent.TimeUnit
11 |
12 | /**
13 | * Created by mind.
14 | */
15 | class WebApiClient {
16 |
17 | companion object {
18 |
19 | val webApiWithoutToken: WebApi by lazy {
20 | val logging = getLoggingInstance()
21 |
22 | // here we are defining client and making configuration for read/connection time out.
23 | // also adding interceptor which we use for api logging.
24 | val client = OkHttpClient.Builder()
25 | .readTimeout(2, TimeUnit.MINUTES)
26 | .connectTimeout(2, TimeUnit.MINUTES)
27 | .addInterceptor { chain ->
28 | val request = chain.request().newBuilder()
29 | .addHeader("Accept", "application/json")
30 | .build()
31 | chain.proceed(request)
32 | }.addInterceptor(logging).build()
33 |
34 |
35 | // preparing retrofit builder and adding
36 | val retrofit = retrofitInstance(client)
37 |
38 | retrofit.create(WebApi::class.java)
39 | }
40 |
41 | private fun getLoggingInstance(): HttpLoggingInterceptor {
42 | val logging = HttpLoggingInterceptor()
43 | if (BuildConfig.DEBUG) {
44 | logging.level = HttpLoggingInterceptor.Level.BODY
45 | } else {
46 | logging.level = HttpLoggingInterceptor.Level.NONE
47 | }
48 |
49 | return logging
50 | }
51 |
52 | private fun retrofitInstance(client: OkHttpClient): Retrofit {
53 | return Retrofit.Builder()
54 | .baseUrl(BASE_URL)
55 | .client(client)
56 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
57 | .addConverterFactory(GsonConverterFactory.create())
58 | .build()
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | 
4 | # Flutter + Kotlin (Flutlin)
5 | ### Aim
6 | To find out the way to utilize flutter UI design benefits while on logical tasks like rest api calling with Kotlin.
7 | - UI design in Flutter
8 | - Rest api calling with Retrofit in Kotlin.
9 |
10 | ### Tech
11 | * FlutterView (https://docs.flutter.io/javadoc/io/flutter/view/FlutterView.html)
12 | * FlutterActivity (https://docs.flutter.io/javadoc/io/flutter/app/FlutterActivity.html)
13 |
14 | ### Steps to follow to use Flutter View
15 | 1) Create Flutter Project.
16 | 2) Open android project in Android Studio.
17 | * Create your own layout for your activity and add simple `FlutterView`
18 | ```
19 |
23 | * Need some initialisation for flutter as per below:
24 | ```
25 | val args = getArgsFromIntent(intent)
26 | FlutterMain.ensureInitializationComplete(applicationContext, args)
27 | * Create `FlutterRunArguments`, provide context and entry point of dart for that view.
28 | ```
29 | val runArguments = FlutterRunArguments()
30 | runArguments.bundlePath = FlutterMain.findAppBundlePath(applicationContext)
31 | runArguments.entrypoint = "main"
32 | * Bind your `FlutterView`
33 | ```
34 | flutterView = findViewById(R.id.flutter_view)
35 | flutterView!!.runFromBundle(runArguments)
36 | * User any channel to communicate with your dart file and activity. In our case, I have used `BasicMessageChannel` to pass string as response.
37 | ```
38 | messageChannel = BasicMessageChannel(flutterView, CHANNEL, StringCodec.INSTANCE)
39 | messageChannel?.send(message)
40 | * Close android project.
41 | 3) Open Flutter Project.
42 | * Initialise `BasicMessageChannel` channel which will be helpful to fetch data shared in our Actitivy.
43 | ```
44 | static const BasicMessageChannel platform =
45 | BasicMessageChannel(_channel, StringCodec());
46 | * Register callback in your `initState()` widget
47 | ```
48 | platform.setMessageHandler(_handlePlatformMessage);
49 | * Create method as per below through which you can parse your string into response model and that you can use to reflect in UI.
50 | ```
51 | Future _handlePlatformMessage(String message) async {
52 | // use setState() to reflect the message in your ui.
53 | }
54 |
55 | # LICENSE!
56 |
57 | Flutlin is [MIT-licensed](/LICENSE).
58 |
59 | # Let us know!
60 | We’d be really happy if you send us links to your projects where you use our component. Just send an email to sales@mindinventory.com And do let us know if you have any questions or suggestion regarding our work.
61 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | import 'dart:async';
6 | import 'dart:convert';
7 |
8 | import 'package:flutter/cupertino.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:flutter/services.dart';
11 |
12 | void main() {
13 | runApp(MyHome());
14 | }
15 |
16 | class MyHome extends StatelessWidget {
17 | @override
18 | Widget build(BuildContext context) {
19 | return MaterialApp(
20 | title: 'Flutlin',
21 | theme: ThemeData(
22 | primarySwatch: Colors.grey,
23 | ),
24 | home: MyUsers(),
25 | debugShowCheckedModeBanner: false,
26 | );
27 | }
28 | }
29 |
30 | class MyUsers extends StatefulWidget {
31 | @override
32 | _MyUsersState createState() => _MyUsersState();
33 | }
34 |
35 | class _MyUsersState extends State {
36 | static const String _channel = 'api';
37 | static const String _emptyMessage = '';
38 | static const BasicMessageChannel platform =
39 | BasicMessageChannel(_channel, StringCodec());
40 | bool apiCallCompleted = false;
41 | List images = [];
42 |
43 | @override
44 | void initState() {
45 | super.initState();
46 | platform.setMessageHandler(_handlePlatformMessage);
47 | }
48 |
49 | /// this function helps to manage the message received under MessageChannel.
50 | Future _handlePlatformMessage(String message) async {
51 | final List imageDynamicList = json.decode(message);
52 | // ignore: always_specify_types
53 | for (var post in imageDynamicList) {
54 | images.add(post.toString());
55 | }
56 | setState(() {
57 | apiCallCompleted = true;
58 | });
59 | return _emptyMessage;
60 | }
61 |
62 | @override
63 | Widget build(BuildContext context) {
64 | return Scaffold(
65 | body: _getScreenBody(context),
66 | );
67 | }
68 |
69 | /// this widget helps to create UI for body part.
70 | Widget _getScreenBody(BuildContext context) {
71 | if (apiCallCompleted) {
72 | return GridView.builder(
73 | gridDelegate:
74 | SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
75 | itemBuilder: _getRowWidget,
76 | itemCount: images.length);
77 | } else {
78 | return Center(child: Text('Calling API...'));
79 | }
80 | }
81 |
82 | /// this widget is used as row tile for GridView.
83 | Widget _getRowWidget(BuildContext context, int index) {
84 | return Padding(
85 | padding: const EdgeInsets.all(0.1),
86 | child: Container(
87 | width: double.infinity,
88 | height: 190.0,
89 | decoration: BoxDecoration(
90 | border: Border.all(color: Colors.grey[350], width: 1.0),
91 | image: DecorationImage(
92 | fit: BoxFit.cover, image: NetworkImage(images[index])))),
93 | );
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutlin
2 | description: A new Flutter application.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.0.0+1
15 |
16 | environment:
17 | sdk: ">=2.1.0 <3.0.0"
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 |
23 | # The following adds the Cupertino Icons font to your application.
24 | # Use with the CupertinoIcons class for iOS style icons.
25 | cupertino_icons: ^1.0.5
26 |
27 | dev_dependencies:
28 | flutter_test:
29 | sdk: flutter
30 |
31 |
32 | # For information on the generic Dart part of this file, see the
33 | # following page: https://www.dartlang.org/tools/pub/pubspec
34 |
35 | # The following section is specific to Flutter.
36 | flutter:
37 |
38 | # The following line ensures that the Material Icons font is
39 | # included with your application, so that you can use the icons in
40 | # the material Icons class.
41 | uses-material-design: true
42 | assets:
43 | - assets/flutter-mark-square-64.png
44 | - assets/loader.gif
45 |
46 | # To add assets to your application, add an assets section, like this:
47 | # assets:
48 | # - images/a_dot_burr.jpeg
49 | # - images/a_dot_ham.jpeg
50 |
51 | # An image asset can refer to one or more resolution-specific "variants", see
52 | # https://flutter.io/assets-and-images/#resolution-aware.
53 |
54 | # For details regarding adding assets from package dependencies, see
55 | # https://flutter.io/assets-and-images/#from-packages
56 |
57 | # To add custom fonts to your application, add a fonts section here,
58 | # in this "flutter" section. Each entry in this list should have a
59 | # "family" key with the font family name, and a "fonts" key with a
60 | # list giving the asset and other descriptors for the font. For
61 | # example:
62 | # fonts:
63 | # - family: Schyler
64 | # fonts:
65 | # - asset: fonts/Schyler-Regular.ttf
66 | # - asset: fonts/Schyler-Italic.ttf
67 | # style: italic
68 | # - family: Trajan Pro
69 | # fonts:
70 | # - asset: fonts/TrajanPro.ttf
71 | # - asset: fonts/TrajanPro_Bold.ttf
72 | # weight: 700
73 | #
74 | # For details regarding fonts from package dependencies,
75 | # see https://flutter.io/custom-fonts/#from-packages
76 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 32
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "com.flutlin"
42 | minSdkVersion 16
43 | targetSdkVersion 31
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
47 | multiDexEnabled true
48 | }
49 |
50 | buildTypes {
51 | release {
52 | // TODO: Add your own signing config for the release build.
53 | // Signing with the debug keys for now, so `flutter run --release` works.
54 | signingConfig signingConfigs.debug
55 | }
56 | }
57 | }
58 |
59 | flutter {
60 | source '../..'
61 | }
62 |
63 | dependencies {
64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
65 | testImplementation 'junit:junit:4.12'
66 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
68 | implementation 'androidx.appcompat:appcompat:1.5.1'
69 | implementation 'com.google.android.material:material:1.6.1'
70 | implementation 'com.google.code.gson:gson:2.8.5'
71 | implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
72 |
73 |
74 | // this library is used for Retrofit API calling feature
75 | implementation "com.squareup.retrofit2:retrofit:2.5.0"
76 | implementation "com.squareup.retrofit2:adapter-rxjava2:2.5.0"
77 | implementation "com.squareup.retrofit2:converter-gson:2.5.0"
78 | implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
79 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
80 | implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
81 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
82 | implementation 'com.android.support:multidex:1.0.3'
83 | }
84 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.2.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.0.4"
18 | charcode:
19 | dependency: transitive
20 | description:
21 | name: charcode
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.2"
25 | collection:
26 | dependency: transitive
27 | description:
28 | name: collection
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.14.11"
32 | cupertino_icons:
33 | dependency: "direct main"
34 | description:
35 | name: cupertino_icons
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "0.1.2"
39 | flutter:
40 | dependency: "direct main"
41 | description: flutter
42 | source: sdk
43 | version: "0.0.0"
44 | flutter_test:
45 | dependency: "direct dev"
46 | description: flutter
47 | source: sdk
48 | version: "0.0.0"
49 | matcher:
50 | dependency: transitive
51 | description:
52 | name: matcher
53 | url: "https://pub.dartlang.org"
54 | source: hosted
55 | version: "0.12.5"
56 | meta:
57 | dependency: transitive
58 | description:
59 | name: meta
60 | url: "https://pub.dartlang.org"
61 | source: hosted
62 | version: "1.1.6"
63 | path:
64 | dependency: transitive
65 | description:
66 | name: path
67 | url: "https://pub.dartlang.org"
68 | source: hosted
69 | version: "1.6.2"
70 | pedantic:
71 | dependency: transitive
72 | description:
73 | name: pedantic
74 | url: "https://pub.dartlang.org"
75 | source: hosted
76 | version: "1.5.0"
77 | quiver:
78 | dependency: transitive
79 | description:
80 | name: quiver
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "2.0.3"
84 | sky_engine:
85 | dependency: transitive
86 | description: flutter
87 | source: sdk
88 | version: "0.0.99"
89 | source_span:
90 | dependency: transitive
91 | description:
92 | name: source_span
93 | url: "https://pub.dartlang.org"
94 | source: hosted
95 | version: "1.5.5"
96 | stack_trace:
97 | dependency: transitive
98 | description:
99 | name: stack_trace
100 | url: "https://pub.dartlang.org"
101 | source: hosted
102 | version: "1.9.3"
103 | stream_channel:
104 | dependency: transitive
105 | description:
106 | name: stream_channel
107 | url: "https://pub.dartlang.org"
108 | source: hosted
109 | version: "2.0.0"
110 | string_scanner:
111 | dependency: transitive
112 | description:
113 | name: string_scanner
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "1.0.4"
117 | term_glyph:
118 | dependency: transitive
119 | description:
120 | name: term_glyph
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "1.1.0"
124 | test_api:
125 | dependency: transitive
126 | description:
127 | name: test_api
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "0.2.5"
131 | typed_data:
132 | dependency: transitive
133 | description:
134 | name: typed_data
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.1.6"
138 | vector_math:
139 | dependency: transitive
140 | description:
141 | name: vector_math
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "2.0.8"
145 | sdks:
146 | dart: ">=2.2.0 <3.0.0"
147 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/flutlin/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.flutlin.activity
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import android.os.Looper
8 | import android.util.Log.d
9 | import androidx.appcompat.app.AppCompatActivity
10 | import com.flutlin.R
11 | import com.flutlin.apiConfig.WebApiClient
12 | import com.flutlin.apiConfig.response.UserResponse
13 | import com.google.gson.GsonBuilder
14 | import io.flutter.embedding.android.FlutterActivity
15 | import io.flutter.plugin.common.BasicMessageChannel
16 | import io.flutter.plugin.common.StringCodec
17 | import io.flutter.view.FlutterMain
18 | import io.flutter.view.FlutterRunArguments
19 | import io.flutter.view.FlutterView
20 | import io.reactivex.android.schedulers.AndroidSchedulers
21 | import io.reactivex.schedulers.Schedulers
22 | import io.flutter.embedding.engine.FlutterEngine
23 | import io.flutter.plugins.GeneratedPluginRegistrant
24 | import java.util.*
25 |
26 | class MainActivity : FlutterActivity() {
27 | private var flutterView: FlutterView? = null
28 | private var messageChannel: BasicMessageChannel? = null
29 | private val CHANNEL = "api"
30 | private var LOG_TAG = "apiCall"
31 |
32 | private fun getArgsFromIntent(intent: Intent): Array? {
33 | // Before adding more entries to this list, consider that arbitrary
34 | // Android applications can generate intents with extra data and that
35 | // there are many security-sensitive args in the binary.
36 | val args = ArrayList()
37 | if (intent.getBooleanExtra("trace-startup", false)) {
38 | args.add("--trace-startup")
39 | }
40 | if (intent.getBooleanExtra("start-paused", false)) {
41 | args.add("--start-paused")
42 | }
43 | if (intent.getBooleanExtra("enable-dart-profiling", false)) {
44 | args.add("--enable-dart-profiling")
45 | }
46 | if (!args.isEmpty()) {
47 | val argsArray = arrayOfNulls(args.size)
48 | return args.toTypedArray()
49 | }
50 | return null
51 | }
52 |
53 |
54 | override fun onCreate(savedInstanceState: Bundle?) {
55 |
56 | FlutterMain.startInitialization(this); //Added line
57 | super.onCreate(savedInstanceState);
58 | val flutterEngine = FlutterEngine(applicationContext)
59 | GeneratedPluginRegistrant.registerWith(flutterEngine);
60 |
61 |
62 |
63 | // handling arguments.
64 | val args = getArgsFromIntent(intent)
65 | FlutterMain.ensureInitializationComplete(applicationContext, args)
66 |
67 | // requesting for layout inside content view.
68 | setContentView(R.layout.flutter_view_layout)
69 |
70 | // hiding action bar
71 |
72 | flutterView = findViewById(R.id.flutter_view)
73 |
74 | // preparing bundle to use for FlutterView we have added inside activity layout.
75 | val runArguments = FlutterRunArguments()
76 | runArguments.bundlePath = FlutterMain.findAppBundlePath(applicationContext)
77 | runArguments.entrypoint = "main"
78 | flutterView!!.runFromBundle(runArguments)
79 |
80 | // establishing message channel which can be used for the data passing from one platform to another.
81 | messageChannel = BasicMessageChannel(flutterView!!,CHANNEL,StringCodec.INSTANCE)
82 |
83 | sendAndroidIncrement("")
84 |
85 | // calling rest api of random user.
86 | callRandomUserApi()
87 | }
88 |
89 | /**
90 | * this function is used to send message on Message Channel with appropriate delay.
91 | */
92 | private fun sendAndroidIncrement(message: String) {
93 | Handler().postDelayed({ messageChannel?.send(message) }, 500)
94 | }
95 |
96 | override fun onPostResume() {
97 | super.onPostResume()
98 | flutterView?.onPostResume()
99 | }
100 |
101 | /**
102 | * here, we are calling random user profile.
103 | */
104 | @SuppressLint("CheckResult")
105 | private fun callRandomUserApi() {
106 | WebApiClient
107 | .webApiWithoutToken
108 | .randomUserApi()
109 | .subscribeOn(Schedulers.io())
110 | .observeOn(AndroidSchedulers.mainThread())
111 | .subscribe(
112 | { result ->
113 | if (result.isSuccessful && result.body() != null) {
114 | val userResponse: UserResponse? = result.body()
115 | userResponse?.let {
116 | it.users?.let {
117 | val userPicList: Array = it.map {
118 | it.picture?.image ?: ""
119 | }.toTypedArray()
120 | val gson = GsonBuilder().setPrettyPrinting().create()
121 | // val dataAsString: String = gson.toJson(images)
122 | val dataAsString: String = gson.toJson(userPicList)
123 | sendAndroidIncrement(dataAsString)
124 | }
125 | }
126 | }
127 | },
128 | { error ->
129 | d(LOG_TAG, "api error: ${error.message} ")
130 | })
131 | }
132 |
133 | override fun onPause() {
134 | super.onPause()
135 | flutterView?.onPause()
136 | }
137 |
138 | override fun onDestroy() {
139 | flutterView?.destroy()
140 | super.onDestroy()
141 | }
142 | }
143 |
144 |
--------------------------------------------------------------------------------