Multiplatform UI DSL with screen management in common code for mobile (android & ios) Kotlin Multiplatform development

Overview

moko-widgets
GitHub license Download kotlin-version

Mobile Kotlin widgets

This is a Kotlin MultiPlatform library that provides declarative UI and application screens management in common code. You can implement full application for Android and iOS only from common code with it.

Current status

Current version - 0.1.0-dev-19. Dev version is not tested in production tasks yet, API can be changed and bugs may be found. But dev version is chance to test limits of API and concepts to feedback and improve lib. We open for any feedback and ideas (go to issues or #moko at kotlinlang.slack.com)!

Roadmap

  • December-January: Test library in real project;
  • February: production usage at IceRock;
  • March: 0.1.0 release with flexible API;
  • First half of 2020: more widgets, more factories; figma template and generation of screens.

Sample Screen

Android iOS
Sample Android Sample iOS
Code of screen structure:
class LoginScreen(
    private val theme: Theme,
    private val loginViewModelFactory: () -> LoginViewModel
) : WidgetScreen<Args.Empty>() {

    override fun createContentWidget() = with(theme) {
        val viewModel = getViewModel(loginViewModelFactory)

        constraint(size = WidgetSize.AsParent) {
            val logoImage = +image(
                size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.WrapContent),
                image = const(Image.resource(MR.images.logo)),
                scaleType = ImageWidget.ScaleType.FIT
            )

            val emailInput = +input(
                size = WidgetSize.WidthAsParentHeightWrapContent,
                id = Id.EmailInputId,
                label = const("Email".desc() as StringDesc),
                field = viewModel.emailField,
                inputType = InputType.PHONE
            )
            val passwordInput = +input(
                size = WidgetSize.WidthAsParentHeightWrapContent,
                id = Id.PasswordInputId,
                label = const("Password".desc() as StringDesc),
                field = viewModel.passwordField
            )
            val loginButton = +button(
                size = WidgetSize.Const(SizeSpec.AsParent, SizeSpec.Exact(50f)),
                content = ButtonWidget.Content.Text(Value.data("Login".desc())),
                onTap = viewModel::onLoginPressed
            )

            val registerButton = +button(
                id = Id.RegistrationButtonId,
                size = WidgetSize.Const(SizeSpec.WrapContent, SizeSpec.Exact(40f)),
                content = ButtonWidget.Content.Text(Value.data("Registration".desc())),
                onTap = viewModel::onRegistrationPressed
            )

            val copyrightText = +text(
                size = WidgetSize.WrapContent,
                text = const("IceRock Development")
            )

            constraints {
                passwordInput centerYToCenterY root
                passwordInput leftRightToLeftRight root offset 16

                emailInput bottomToTop passwordInput offset 8
                emailInput leftRightToLeftRight root offset 16

                loginButton topToBottom passwordInput
                loginButton leftRightToLeftRight root

                registerButton topToBottom loginButton
                registerButton rightToRight root

                // logo image height must be automatic ?
                logoImage centerXToCenterX root
                logoImage.verticalCenterBetween(
                    top = root.top,
                    bottom = emailInput.top
                )

                copyrightText centerXToCenterX root
                copyrightText bottomToBottom root.safeArea offset 8
            }
        }
    }

    object Id {
        object EmailInputId : InputWidget.Id
        object PasswordInputId : InputWidget.Id
        object RegistrationButtonId : ButtonWidget.Id
    }
}

Code of theme:

val loginScreen = Theme(baseTheme) {
    factory[ConstraintWidget.DefaultCategory] = ConstraintViewFactory(
        padding = PaddingValues(16f),
        background = Background(
            fill = Fill.Solid(Colors.white)
        )
    )

    factory[InputWidget.DefaultCategory] = SystemInputViewFactory(
        margins = MarginValues(bottom = 8f),
        underLineColor = Color(0x000000DD),
        underLineFocusedColor = Color(0x3949ABFF),
        labelTextStyle = TextStyle(
            size = 12,
            color = Color(0x3949ABFF),
            fontStyle = FontStyle.BOLD
        ),
        errorTextStyle = TextStyle(
            size = 12,
            color = Color(0xB00020FF),
            fontStyle = FontStyle.BOLD
        ),
        textStyle = TextStyle(
            size = 16,
            color = Color(0x000000FF),
            fontStyle = FontStyle.MEDIUM
        )
    )

    val corners = platformSpecific(android = 8f, ios = 25f)

    factory[ButtonWidget.DefaultCategory] = SystemButtonViewFactory(
        margins = MarginValues(top = 32f),
        background = {
            val bg: (Color) -> Background = {
                Background(
                    fill = Fill.Solid(it),
                    shape = Shape.Rectangle(
                        cornerRadius = corners
                    )
                )
            }
            StateBackground(
                normal = bg(Color(0x6770e0FF)),
                pressed = bg(Color(0x6770e0EE)),
                disabled = bg(Color(0x6770e0BB))
            )
        }.invoke(),
        textStyle = TextStyle(
            color = Colors.white
        )
    )

    factory[LoginScreen.Id.RegistrationButtonId] = SystemButtonViewFactory(
        background = {
            val bg: (Color) -> Background = {
                Background(
                    fill = Fill.Solid(it),
                    shape = Shape.Rectangle(
                        cornerRadius = corners
                    )
                )
            }
            StateBackground(
                normal = bg(Color(0xFFFFFF00)),
                pressed = bg(Color(0xE7E7EEEE)),
                disabled = bg(Color(0x000000BB))
            )
        }.invoke(),
        margins = MarginValues(top = 16f),
        textStyle = TextStyle(
            color = Color(0x777889FF)
        ),
        androidElevationEnabled = false
    )
}

Table of Contents

Features

  • compliance with platform rules;
  • declare structure, not rendering;
  • compile-time safety;
  • reactive data handling.

Requirements

  • Gradle version 5.6.4+
  • Android API 16+
  • iOS version 9.0+

Versions

  • kotlin 1.3.50
    • 0.1.0-dev-1
  • kotlin 1.3.60
    • 0.1.0-dev-2
    • 0.1.0-dev-3
    • 0.1.0-dev-4
    • 0.1.0-dev-5
  • kotlin 1.3.61
    • 0.1.0-dev-6
    • 0.1.0-dev-7
    • 0.1.0-dev-8
    • 0.1.0-dev-9
    • 0.1.0-dev-10
    • 0.1.0-dev-11
    • 0.1.0-dev-12
    • 0.1.0-dev-13
    • 0.1.0-dev-14
    • 0.1.0-dev-15
  • kotlin 1.3.70
    • 0.1.0-dev-16
    • 0.1.0-dev-17
    • 0.1.0-dev-18
    • 0.1.0-dev-19

Installation

root build.gradle

allprojects {
    repositories {
        maven { url = "https://dl.bintray.com/icerockdev/moko" }
    }
}

project build.gradle

dependencies {
    commonMainApi("dev.icerock.moko:widgets:0.1.0-dev-19")
}

Codegen for new Widgets with @WidgetDef

root build.gradle

buildscript {
    repositories {
        maven { url = "https://dl.bintray.com/icerockdev/plugins" } // gradle plugin
    }

    dependencies {
        classpath "dev.icerock.moko.widgets:gradle-plugin:0.1.0-dev-19"
    }
}

allprojects {
    repositories {
        maven { url = uri("https://dl.bintray.com/icerockdev/plugins") } // compiler plugins
    }
}

project build.gradle

apply plugin: "dev.icerock.mobile.multiplatform-widgets-generator" // must apply before kotlin-multiplatform plugin

Usage

Hello world

Multiplatform application definition at mpp-library/src/commonMain/kotlin/App.kt:

class App : BaseApplication() {
    override fun setup(): ScreenDesc<Args.Empty> {
        val theme = Theme()

        return registerScreen(HelloWorldScreen::class) { HelloWorldScreen(theme) }
    }
}

Screen definition mpp-library/src/commonMain/kotlin/HelloWorldScreen.kt:

class HelloWorldScreen(
    private val theme: Theme
) : WidgetScreen<Args.Empty>() {

    override fun createContentWidget() = with(theme) {
        container(size = WidgetSize.AsParent) {
            center {
                text(
                    size = WidgetSize.WrapContent,
                    text = const("Hello World!")
                )
            }
        }
    }
}

Result:

Android iOS
HelloWorld Android HelloWorld iOS

Configure styles

Setup theme config:

val theme = Theme {
    factory[TextWidget.DefaultCategory] = SystemTextViewFactory(
        textStyle = TextStyle(
            size = 24,
            color = Colors.black
        ),
        padding = PaddingValues(padding = 16f)
    )
}

Result:

Android iOS
Custom style Android Custom style iOS

Bind data to UI

class TimerScreen(
    private val theme: Theme
) : WidgetScreen<Args.Empty>() {
    override fun createContentWidget(): Widget<WidgetSize.Const<SizeSpec.AsParent, SizeSpec.AsParent>> {
        val viewModel = getViewModel { TimerViewModel() }

        return with(theme) {
            container(size = WidgetSize.AsParent) {
                center {
                    text(
                        size = WidgetSize.WrapContent,
                        text = viewModel.text
                    )
                }
            }
        }
    }
}

class TimerViewModel : ViewModel() {
    private val iteration = MutableLiveData<Int>(0)
    val text: LiveData<StringDesc> = iteration.map { it.toString().desc() }

    init {
        viewModelScope.launch {
            while (isActive) {
                delay(1000)
                iteration.value = iteration.value + 1
            }
        }
    }
}

Samples

Please see more examples in the sample directory.

Set Up Locally

Contributing

All development (both new features and bug fixes) is performed in the develop branch. This way master always contains the sources of the most recently released version. Please send PRs with bug fixes to the develop branch. Documentation fixes in the markdown files are an exception to this rule. They are updated directly in master.

The develop branch is pushed to master on release.

For more details on contributing please see the contributing guide.

License

Copyright 2019 IceRock MAG Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Help set formatted value to TextWidget

    Help set formatted value to TextWidget

    Hi, I try convert value to byte size to Gb and set to TextWidget in UnitItem. But in block text = data.map { ... } I get an error. And I don't understand how add in this block value + "String" like text = data.map { it.total.desc() + "Gb" as StringDesc() } Always return Error. In Android I use android.text.format.Formatter.formatShortFileSize and all works.

    Value("total" in Kb): Снимок экрана 2020-07-08 в 12 27 57

    My List: Снимок экрана 2020-07-08 в 12 29 26

    My fun TableUnits: Снимок экрана 2020-07-08 в 12 29 37

    Class unit items where I create TextWidget: Снимок экрана 2020-07-08 в 12 28 42

    Convert fun to Mb/Gb: Снимок экрана 2020-07-08 в 12 28 20

    question 
    opened by Diy2210 10
  • XCode build failed

    XCode build failed

    Hi, I run pod install but console show Failure: Build failed Снимок экрана 2020-07-02 в 12 48 41 But Pod installation is complete!

    And them Xcode show build failed too: Снимок экрана 2020-07-02 в 12 48 09 Снимок экрана 2020-07-02 в 12 48 53

    How I fix it?

    question 
    opened by Diy2210 10
  • Help fix constraint elements

    Help fix constraint elements

    How fix it?

    Android: Снимок экрана 2020-07-24 в 15 42 11

    iOS: Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-24 at 15 46 51

    Constraint Item: Снимок экрана 2020-07-24 в 15 46 19

    Constraint: Снимок экрана 2020-07-24 в 15 46 34

    And one more question why button style in iOS another? How fix it too? Left iOS, right Android: Снимок экрана 2020-07-24 в 15 51 38

    Style Cancel button: Снимок экрана 2020-07-24 в 15 53 21

    Style Save button: Снимок экрана 2020-07-24 в 15 53 29

    question 
    opened by Diy2210 5
  • Help add MutableLiveData from ListWidget

    Help add MutableLiveData from ListWidget

    Hi, I try add MutableLiveData use Database, but I don't understand how.

    My List in database: Снимок экрана 2020-07-20 в 11 52 56

    Database class: Снимок экрана 2020-07-20 в 11 54 58

    My old MutableLiveData in ViewModelClass: Снимок экрана 2020-07-20 в 11 57 35

    UnitItem class: Снимок экрана 2020-07-20 в 11 54 36

    And use in ListWidget: Снимок экрана 2020-07-20 в 11 55 31 Снимок экрана 2020-07-20 в 11 55 41

    question 
    opened by Diy2210 5
  • Add support of runtime Id creation

    Add support of runtime Id creation

    I updated my project based on the recent commit of moko-widgets-template, so using 0.1.0-dev-18 now. And my code previously working with -dev12 throws an exception on Android run now.

    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: org.example.app.debug, PID: 18128
        dev.icerock.moko.widgets.utils.AndroidIdConflictException: id 0x7F081AB9 already used by org.example.mpp.MainScreen$createInput$1$2, it conflict with org.example.mpp.MainScreen$createInput$1$2
            at dev.icerock.moko.widgets.utils.IdExtKt.getAndroidId(IdExt.kt:45)
    ...
    

    I stripped down the test case to minimum for reproducing the error:

    • use the current moko-widgets-template
    • replace MainScreen.kt with:
    /*
     * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
     */
    
    package org.example.mpp
    
    import dev.icerock.moko.fields.FormField
    import dev.icerock.moko.fields.liveBlock
    import dev.icerock.moko.resources.desc.StringDesc
    import dev.icerock.moko.resources.desc.desc
    import dev.icerock.moko.widgets.*
    import dev.icerock.moko.widgets.core.Theme
    import dev.icerock.moko.widgets.core.Widget
    import dev.icerock.moko.widgets.screen.Args
    import dev.icerock.moko.widgets.screen.WidgetScreen
    import dev.icerock.moko.widgets.style.view.SizeSpec
    import dev.icerock.moko.widgets.style.view.WidgetSize
    import org.example.library.MR
    
    class MainScreen(
        private val theme: Theme
    ) : WidgetScreen<Args.Empty>() {
    
        private fun createInput() : Widget<*>{
            return with(theme) {
                input(
                    size = WidgetSize.WrapContent,
                    label = const(MR.strings.hello_world.desc() as StringDesc),
                    field = FormField(initialValue = "", validation = liveBlock { null }),
                    id = object : InputWidget.Id {}
                )
            }
        }
    
        override fun createContentWidget() = with(theme) {
            constraint(size = WidgetSize.AsParent) {
                val scroll = +scroll(
                    size = WidgetSize.Const(SizeSpec.MatchConstraint, SizeSpec.MatchConstraint),
                    id = object : ScrollWidget.Id {},
                    child = linear(
                        size = WidgetSize.Const(SizeSpec.AsParent, SizeSpec.WrapContent)
                    ) {
                        +createInput()
                        +createInput()
                        +createInput()
                    })
    
                constraints {
                    scroll.topToTop(root.safeArea)
                    scroll.leftRightToLeftRight(root.safeArea)
                    scroll.bottomToBottom(root.safeArea)
                }
            }
        }
    }
    
    
    • run in Android emulator

    The input fields are generated dynamically, the id of the returned input field is a new object, but it seems that the moko-widgets lib thinks it's the same. Or is the code just wrong and it's not the right way to do it?

    enhancement 
    opened by Gamadril 5
  • Help check value and set image to ImageWidget

    Help check value and set image to ImageWidget

    Hi, how I can check value in override fun createWidget like if/else or something like that and select one or another image.

    I use List Widget and Table Units:

    My ListWidget: Снимок экрана 2020-07-07 в 12 58 53

    Fun Table Units: Снимок экрана 2020-07-07 в 12 59 01

    My class Unit Items where I make image: Снимок экрана 2020-07-07 в 12 59 20

    Result: Снимок экрана 2020-07-07 в 12 59 39

    question 
    opened by Diy2210 4
  • System Navbar customisation

    System Navbar customisation

    min. requirements:

    background (bg color), text customisation (size, color) left icon customisation (margin, resource) right icon support and customisation (margin, resource)

    maybe immediately add customisation status bar? (light/dark + bg color and opacity)

    enhancement 
    opened by Anafemest 4
  • Improve navigation

    Improve navigation

    For now Screen have property parentScreen to communicate between screens in navigation. It's done this way because android side can't pass lambda/interface to another screen - Screen is Fragment on android, and can be recreated on orientation change or application restart.

    Current version of navigation:

    class RootNavigationScreen(
        screenFactory: ScreenFactory
    ) : NavigationScreen(screenFactory), SplashScreen.Parent {
        override val rootScreen = SplashScreen::class.rootNavigationScreen()
    
        override fun routeToMainScreen() {
            setScreen(MainScreen::class)
        }
    }
    
    class SplashScreen(
        private val theme: Theme
    ) : WidgetScreen<Args.Empty>(), SplashViewModel.EventsListener, NavigationItem {
    
        override val navigationBar: NavigationBar = NavigationBar.None
    
        override fun createContentWidget(): Widget<WidgetSize.Const<SizeSpec.AsParent, SizeSpec.AsParent>> {
            val viewModel = getViewModel {
                SplashViewModel(createEventsDispatcher())
            }
            viewModel.eventsDispatcher.listen(this, this)
    
            // some widget here
        }
    
        override fun routeToMain() {
            getParentScreen<Parent>().routeToMainScreen()
        }
    
        interface Parent {
            fun routeToMainScreen()
        }
    }
    
    class SplashViewModel(
        override val eventsDispatcher: EventsDispatcher<EventsListener>,
    ) : ViewModel(), EventsDispatcherOwner<SplashViewModel.EventsListener> {
    
        init() {
            viewModelScope.launch {
                delay(1000)
                eventsDispatcher.dispatchEvent { routeToMain() }
            }
        }
    
        interface EventsListener {
            fun routeToMain()
        }
    }
    

    here we should declare navigation event multiple times - first in EventsListener of SplashViewModel (it's source of event), next in Parent interface of SplashScreen - it's will be called when ViewModel call own routeToMain. and routing implemented in RootNavigationScreen.

    new version of navigation should:

    • allow use same screen in different navigations containers (for example UserScreen as tab in BottomNavigation and as independent screen in nested navigation when open different user profile;
    • not require duplication of interfaces (remove Parent interface and child-parent communication);
    • have compile-time typecheck - compilation should failed if we try show SplashScreen from RootNavigationScreen which can't recognize navigation events of SplashScreen;
    • allow use deep navigation - BottomNavigationScreen => NavigationScreen => PostScreen should can navigate to other tab of BottomNavigationScreen.
    enhancement 
    opened by Alex009 4
  • ListWidget on iOS dont work

    ListWidget on iOS dont work

    Hi, I generate iOS app and ListWidget dont show items. In Android all works I dond understand why.

    My ListWidget: Снимок экрана 2020-07-23 в 14 42 42

    TableUnits: Снимок экрана 2020-07-23 в 14 42 50

    UnitItem class: Снимок экрана 2020-07-23 в 14 43 34

    Value in Xcode: Снимок экрана 2020-07-23 в 14 44 29

    Result iOS: Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-23 at 14 44 33

    Android: Screenshot_1595504680

    And round button on iOS is strange =)

    opened by Diy2210 3
  • Invalid android Id generation

    Invalid android Id generation

    Now android id set from hashcode of Id object class name and android can't recognize part. As result we see:

    java.lang.ClassCastException: android.widget.TextView$SavedState cannot be cast to android.widget.ScrollView$SavedState
    

    and invalid constraintlayout work: image

    bug 
    opened by Alex009 3
  • Add listener from Widget-List

    Add listener from Widget-List

    How implement item list click listener? I create list on widgets like codelabs(MOKO Widgets #7 - lists on widgets), all works, but i dont understand how add listener from item click. Снимок экрана 2020-06-10 в 16 46 24

    My code: Снимок экрана 2020-06-10 в 16 58 47 Снимок экрана 2020-06-10 в 16 58 54 My ViewModel: Снимок экрана 2020-06-10 в 16 59 42

    UnitItem: Снимок экрана 2020-06-10 в 17 00 25

    question 
    opened by Diy2210 2
  • Migrate iOS factories to Swift

    Migrate iOS factories to Swift

    maintaining Kotlin version of iOS UI code is painful (rebuild kotlin framework is long, full debugging still unsupported). We should migrate to Swift version of ios WidgetFactories to boost changes of ios side ui code.

    Will create swift cocoapod and this pod will be used from kotlin side

    enhancement 
    opened by Alex009 0
  • Remove displayLink usage

    Remove displayLink usage

    now in some ui code used displayLink for valid update view properties. it's very bad for perfomance and i think it create some memory leaks, so we should remove usage of it

    enhancement 
    opened by Alex009 0
  • [WIP] Custom fonts support in TextStyle fixes #6

    [WIP] Custom fonts support in TextStyle fixes #6

    Hi,

    this is draft of support for custom fonts in TextStyle. My main concern now is about how to ( or even should we ) support custom font with and ** fontStyle** at the same time. Basically we can include different types of fonts with only one text style, or with multiple font styles. I think that currently there is no even easy way to do it on any of the platforms. We can use typeface on android and symbolicTraits on iOS and add some fallbacks to default font. But personally it think that (at least for now) we should skip fontStyle when custom font is set. Main reasons for that:

    • mocko-resources requires adding font files with single fontstyle
    • combining both properties will add additional complexity
    • in most cases developers will add/set custom font with given font style directly ( not by combining both properties)

    What do you think ?

    I will continue my work, when there will be decision which path should we follow.

    opened by kkalisz 0
  • Disable scroll from ListWidget

    Disable scroll from ListWidget

    How disable scroll from ListWidget inside Scroll Code: scroll( id = Ids.RootScroll, size = AsParent, child = linear( id = Ids.RootLinearId, size = WidthAsParentHeightWrapContent ) { ... }

    Снимок экрана 2020-07-10 в 17 34 22

    My ListWidget inside Constraint: Снимок экрана 2020-07-10 в 17 34 03

    I try change Size from List but scroll is enabled too.

    Server Status Block: Снимок экрана 2020-07-10 в 17 33 00 Снимок экрана 2020-07-10 в 17 32 40

    question 
    opened by Diy2210 5
  • Implement push notification

    Implement push notification

    1)How implement push notification on common code? Or I use only native android/ios code? How about MOKO-Notifications? =) 2) What database can I use in common code? And SQL Database implement MOKO in future? Снимок экрана 2020-07-10 в 15 24 55

    question 
    opened by Diy2210 1
Releases(release/0.1.0)
  • release/0.1.0(Jul 29, 2021)

  • release/0.1.0-dev-20(Aug 30, 2020)

    last version on kotlin 1.3.72 before update to kotlin 1.4.0. readme is outdated, changelog only in milestone (https://github.com/icerockdev/moko-widgets/milestone/15) issues list

    contains breaking changes (compare to 0.1.0 dev 19)

    some later will be updated readme. or it will be updated with 0.1.0 on kotlin 1.4.0

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-19(Mar 24, 2020)

  • release/0.1.0-dev-18(Mar 19, 2020)

  • release/0.1.0-dev-17(Mar 16, 2020)

  • release/0.1.0-dev-16(Mar 16, 2020)

    Changes

    • kotlin 1.3.70
    • android gradle plugin 3.6.1
    • gradle 5.6.4
    • klock 1.9.1
    • coroutines 1.3.4
    • fix iOS 12 crash #187
    • fix iOS toast overlay #186

    Thanks

    @Dorofeev

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-15(Mar 6, 2020)

    Changes

    moko-widgets

    • CollectionWidget moved to moko-widgets-collection #33
    • DatePickerDialog moved to moko-widgets-datetime-picker #171
    • TemplateScreen as placeholder #35
    • fix images scaling on android #162
    • maxLines support on TextWidget #161
    • fix image tint on button with image #95
    • fix save instance state on android #61
    • added Screen.onViewCreated callback #178
    • klock dependency removed #171
    • added italic FontStyle #170

    moko-widgets-bottomsheet

    • add dismiss action #158

    moko-widgets-collection (new)

    • support of constant sized units in collections on ios #33

    moko-widgets-datetime-picker (new)

    • TimePickerDialog added #171

    Breaking Changes

    • CollectionWidget moved to moko-widgets-collection;
    • DatePickerDialog moved to moko-widgets-datetime-picker;
    • WidgetsCollectionUnitItem now allow any Widget, not limited by UnitItemRoot;
    • SystemCollectionViewFactory moved to moko-widgets-collection and renamed to SimpleCollectionViewFactory;
    • SimpleCollectionViewFactory not have spanCount anymore.

    Thanks

    @ATchernov @AppoNut @Dorofeev @Tetraquark @Lobynya

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-14(Feb 26, 2020)

    Changes

    moko-widgets

    • added isShadowEnabled to NavigationBar.Styles (required for #131 );
    • added dialPhone screen extension #104 ;
    • added sendEmail screen extension #105 .
    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-13(Feb 25, 2020)

    Changes

    Common

    • added ./publishToMavenLocal.sh script - all parts build to maven local.

    moko-widgets

    • added dependency to klock (com.soywiz.korlibs.klock:klock);
    • up fragment (androidx.fragment:fragment) version to 1.2.2;
    • fix routeWithResult crash on android #128;
    • add datepicker screen action #90;
    • add NavigationBar.Search #100;
    • fix password field on iOS #135;
    • improve tabs customization #131;
    • add multiline input factory #133;
    • rework styles background and stateful #138;
    • replace FrameLayout to FragmentContainerView (as recommended by google) #127;
    • flatAlert replaced to composition of other widgets.

    Breaking changes

    • Removed ApplicationHolderKt.application = app from ios-app;
    • Removed CornerRadiusValue type - now just use Float;
    • StateBackground replaced to PressableState<Background<T>>;
    • State dependend styles use generic types - PressableState, SelectableState and others;
    • flatAlert replaced to composition of other widgets;
    • NavigationBar.Normal.Styles moved to NavigationBar.Styles;
    • NavigationBar.Normal.BarButton moved to NavigationBar.BarButton;
    • TextStyle now have generic type of color (to support stateful and stateless cases);
    • Background now have generic type of fill (to set only Solid color for some widgets, that unsupport sublayers on iOS).

    Thanks

    @Dorofeev @Lobynya @kovalandrew @AppoNut

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-12(Feb 19, 2020)

    Changes

    moko-widgets

    • fix iOS toast size #107
    • fix iOS 12 progressbar visibility #110
    • InputType allow change of mask #99
    • status bar styles control #102
    • fix iOS button's tap and dismiss keyboard #108
    • fix Android crash on screen actions in invalid moment #97
    • fix iOS wrapContent size compression #117
    • added ripple to clickable on Android #91
    • added popToRoot to NavigationScreen.Router #92
    • removed ripple of root view on Android #18
    • fix Android backstack state restorage #124
    • added dismiss keyboard action #112
    • fix resizing of screen with keyboard on Android #94

    moko-widgets-bottomsheet

    • added showBottomSheet action (module moko-widgets-bottomsheet) #73

    moko-widgets-sms

    • added one-time-password support to input (module moko-widgets-sms) #74

    Breaking changes

    • Add on ios-app in delegate ApplicationHolderKt.application = app after App creating;
    • InputType changed from enum to sealed class, so you should change InputType.PHONE to InputType.Phone() and other same way;
    • now iOS Screen class should implement fun createViewController(isLightStatusBar: Boolean?): UIViewController instead fun createViewController(): UIViewController.

    also in ios-app/src/Info.plist should be added:

    <key>UIViewControllerBasedStatusBarAppearance</key>
    <true/>
    

    Thanks

    @Dorofeev , @ATchernov , @Lobynya , @kovalandrew

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-11(Feb 11, 2020)

    • implemented AutoScrollListFactory #50
    • fix input types on ios #63
    • add redirect handling to WebView #66
    • add button with icon factory #27
    • add show dialog and API for dialog actions handling #4
    • add bottom sheet icons selected/unselected #26
    • add navbar styles #9
    • rounded corners on image #78
    • remove source from Route #83
    • fix android back button #25

    libs:

    breaking changes:

    • BaseApplication.setup() call on iOS and android should be replaced to BaseApplication.initialize()
    • Route.route not have source argument now. but RouteWithResult.route have.

    thx to @Dorofeev @Lobynya @kovalandrew @srybakov92 @prokopishin

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-10(Feb 5, 2020)

    • BottomNavigation styles customization #48
    • WebView widget #55
    • fix bottom navigation selected tab restore state on android #56
    • temporary fix of android ids #61
    • fix linear margins on ios #53

    thx to @Tetraquark , @Lobynya

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-9(Jan 31, 2020)

  • release/0.1.0-dev-8(Jan 31, 2020)

    • #31 show toast implementation
    • #38 fix image button tint on iOS
    • #36 fix crash on iOS 12
    • #40 input widget have new system-default factory, old version moved to FloatingLabelInputViewFactory

    thx to @kovalandrew , @Tetraquark , @Dorofeev

    Source code(tar.gz)
    Source code(zip)
  • release/0.1.0-dev-7(Jan 28, 2020)

  • release/0.1.0-dev-6(Jan 22, 2020)

    backward incompatible changes

    widgets:

    • category styling (priority: id > category > DefaultCategory)
    • new navigation #3
    • constraint SizeSpec #8
    • show dialogs from screen support #4
    • screen results pass #7
    • input underline bugfix #10
    • input font style fixes #12
    • button elevation controls #14
    • moko dependencies versions update
    • image button support
    • tabs DSL improved
    • navigation bar hide support
    • new visibility widget
    • keyboard controls in WidgetScreen

    widgets-flat:

    • added new widgets-flat with custom ViewFactories for flat design

    plugin:

    • improve generics support in widgets
    • added category usage
    • added custom default factory name support (argument in @WidgetDef)

    sample:

    • platform-screen and platform-widget-factory samples
    Source code(tar.gz)
    Source code(zip)
Owner
IceRock Development
Kotlin Multiplatform developers team
IceRock Development
A customizable debug screen to view and edit flags that can be used for development in Jetpack Compose applications

Tweaks A customizable debug screen to view and edit flags that can be used for development in Jetpack Compose applications To include the library add

Guillermo Merino Jiménez 4 Jan 14, 2022
Android library for creating an expandable to full screen view inside a viewgroup composition.

Expandable Panel Android Library Check ExpandablePanel Demo application on GooglePlay: Details This Android library implements the expand by sliding l

Jorge Castillo 422 Nov 10, 2022
PCard Add payment card screen made using JetPack Compose

PCard Add payment card screen made using JetPack Compose Find this repository useful? ❤️ Support it by joining stargazers for this repository. ⭐ And f

Mohamed Elbehiry 61 Dec 16, 2022
A simple screen that is shown when your app gets crashed instead of the normal crash dialog. It's very similar to the one in Flutter.

Red Screen Of Death What A simple screen that is shown when your app gets crashed instead of the normal crash dialog. It's very similar to the one in

Ahmad Melegy 178 Dec 9, 2022
Displays your screen time in a permanent notification.

Screen Time Displays your screen time in a permanent notification. By making screen time more prominent, you can get a better sense of how much of the

Markus Fisch 24 Nov 29, 2022
This is a sample Android Studio project that shows the necessary code to create a note list widget, And it's an implementation of a lesson on the Pluralsight platform, but with some code improvements

NoteKeeper-Custom-Widgets This is a sample Android Studio project that shows the necessary code to create a note list widget, And it's an implementati

Ibrahim Mushtaha 3 Oct 29, 2022
An Android library supports badge notification like iOS in Samsung, LG, Sony and HTC launchers.

ShortcutBadger: The ShortcutBadger makes your Android App show the count of unread messages as a badge on your App shortcut! Supported launchers: Sony

Leo Lin 7.2k Dec 30, 2022
ios UISegmentedControl for android

android-segmented-control Android-Segmented is a custom view for Android which is based on RadioGroup and RadioButton widget. This implementation is i

Kaopiz Software Co., Ltd. 1.9k Dec 12, 2022
ios UISegmentedControl for android

android-segmented-control Android-Segmented is a custom view for Android which is based on RadioGroup and RadioButton widget. This implementation is i

Kaopiz Software Co., Ltd. 1.9k Dec 12, 2022
iOS UIActionSheet for Android

ActionSheet This is like iOS UIActionSheet component, has iOS6 and iOS7 style, support custom style, background, button image, text color and spacing

星一 810 Nov 10, 2022
A Popover Controller for Android Tablets. It's an easy solution to simulate an iOS UIPopoverController

PopoverView A Popover Controller for Android Tablets. It's an easy solution to simulate an iOS UIPopoverController Base example 9patch image comes fro

Daniel Lupiañez Casares 203 Nov 29, 2022
A realtime blurring overlay for Android (like iOS UIVisualEffectView)

RealtimeBlurView It's just a realtime blurring overlay like iOS UIVisualEffectView. Just put the view in the layout xml, no Java code is required. //

Tu Yimin 3k Jan 9, 2023
Simple and powerful library to emulate iOS's "3D Touch" preview functionality on Android.

Android 3D Touch - PeekView iOS uses 3D Touch as a way to "peek" into full content, such as emails, pictures, web searches, etc. While they have dedic

Luke Klinker 502 Dec 29, 2022
A Jetpack Compose library with blur, pixelate, and other effects to keep your designer happy. Inspired by iOS UIVisualEffectView.

A Jetpack Compose library with blur, pixelate, and other effects to keep your designer happy. Inspired by iOS UIVisualEffectView.

清茶 67 Dec 30, 2022
Bubbles for Android is an Android library to provide chat heads capabilities on your apps. With a fast way to integrate with your development.

Bubbles for Android Bubbles for Android is an Android library to provide chat heads capabilities on your apps. With a fast way to integrate with your

Txus Ballesteros 1.5k Jan 2, 2023
[] A fast PDF reader component for Android development

This project is no longer maintained. You can find a good replacement here, which is a fork relying on Pdfium instead of Vudroid/MuPDF for decoding PD

Joan Zapata 2.8k Dec 16, 2022
GreenDroid is a development library for the Android platform. It makes UI developments easier and consistent through your applications.

#GreenDroid Foreword : This project, initially initiated by me, Cyril Mottier, is not maintained anymore and can be considered as deprecated. As a con

Cyril Mottier 2.6k Jan 4, 2023
Beagle is an open-source framework for cross-platform development using the concept of Server-Driven UI.

Beagle Getting Started · Learn the Basics · Contribute Beagle is an open-source framework for cross-platform development using the concept of Server-D

ZUP IT INNOVATION 657 Dec 28, 2022
MIUINativeNotifyIcon - Fix the native notification bar icon function abandoned by the MIUI development team

MIUI 原生通知图标 Fix the native notification bar icon function abandoned by the MIUI

Fankesyooni 189 Jan 4, 2023