├── 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 | ![N|Solid](flutter.png)![N|Solid](plus.png)![N|Solid](kotlin.png) 2 | 3 | ![N|Solid](sample.gif) 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 | --------------------------------------------------------------------------------