Skip to content

Latest commit

 

History

History
550 lines (385 loc) · 13.8 KB

README.md

File metadata and controls

550 lines (385 loc) · 13.8 KB

DroidLibs

DroidLibs is an Android Library consisting of several tools and ready to use UI components that ease in the development of modern Android apps. This library consists of multiple modules. You can selectively add the modules as your project's dependencies based on your needs.

Setup

  1. Add Jitpack to your setting.gradle.kts :

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
            maven("https://jitpack.io") // <- Add this
        }
    }
  2. Add dependencies of required modules in your build.gradle.kts :

    Release

    dependencies {
       // base module
       implementation("com.github.The-Streamliners.DroidLibs:base:<latest-version>")
    
       // compose-android module (strictly for Android projects)
       implementation("com.github.The-Streamliners.DroidLibs:compose-android:<latest-version>")
    
       // compose module (strictly for Compose Desktop projects)
       implementation("com.github.The-Streamliners.DroidLibs:compose:<latest-version>")
    
       // helpers module
       implementation("com.github.The-Streamliners.DroidLibs:helpers:<latest-version>")
    
       // pickers module
       implementation("com.github.The-Streamliners.DroidLibs:pickers:<latest-version>")
    
       // utils module
       implementation("com.github.The-Streamliners.DroidLibs:utils:<latest-version>")
    }

Additional step for compose-android module

You might face a common issue while building an app that uses compose-android module i.e. checkDebugDuplicateClasses :

To fix this issue, add this to your app's build.gradle.kts :

configurations {
    "implementation" {
        exclude("org.jetbrains.compose.material", "material-desktop")
    }
}

Or if you're using Groovy gradle :

configurations {
    api {
        exclude group: 'org.jetbrains.compose.material', module: 'material-desktop'
    }
}

Introduction

base module

Base module consists of some very basic yet powerful constructs :

When implemented, these help in emitting UI events from ViewModel to Activity (View). UI events supported :

  • Showing Toast

  • Showing Message Dialog

  • Showing Confirmation Dialog

  • Showing Descriptive Error Dialog

  • Showing Loading (Progress Indicator) Dialog

For each of these, you can invoke a simple function directly from your ViewModel :

class YourAmazingViewModel: BaseViewModel {

    fun yourFun() {
        showToast("Namaste World!")

        showMessageDialog("Registration Successful", "Welcome to DroidLibs!")



        showConfirmationDialog(
            title = "Confirm delete",
            message = "Are you sure? This can't be undone!",
            onConfirm = { userRepo.delete(phoneNo) }
        )

        showLoader("Launching rocket...")
    }
}


TaskState class and its related functions makes it easy to work with Asynchronous tasks. The result of which, can either be Success or Failure. It provides a generic class so you can define it based on your result class.

// 1. Define state
val fetchFactTask = taskStateOf<String>() // Here result is String

// 2. Composables 

// TaskLoadingButton (Displays Progress Indicator while loading)
TaskLoadingButton(
    state = fetchFactTask,
    label = "Fetch",
    onClick = fetchFact
)

// Displayed when loaded
fetchFactTask.whenLoaded { fact -> /* Display fact */ }

// Displayed when error
fetchFactTask.whenError { error -> /* Display error */ }

// 3. Execute task handling errors
fun fetchFact() {
    execute(fetchFactTask) {
        factRepo.getFact(numberInput.value())
    }
}


Provides execute() functions with in-built error handling mechanisms.

class MainViewModel : BaseViewModel() {

    fun registerUser(phoneNo: String, name: String) {
        execute {
            if (userRepo.userExists(phoneNo)) failure("User with phoneNo already exists!")
            userRepo.registerUser(phoneNo, name)
        }
    }
}


Provides functions like :

  • log() to log from anywhere in the app

  • defaultContext() to get a CoroutineContext with exception handler

  • defaultExceptionHandler() to get a CoroutineExceptionHandler

  • Execute function - defaultExecuteHandlingError() with error handling support


compose module

Compose module consists of several commonly used Composables :

TextInputLayout is an advanced TextField with several validation options :

TextInputLayoutDemo.mp4
  1. Defining State

    val nameInput = remember {
        mutableStateOf(
            TextInputState(
                label = "Name",
                inputConfig = InputConfig.text {
                    optional = true
                    minLength = 5
                    maxLength = 30
                }
            )
        )
    }
    
    val aadharNoInput = remember {
        mutableStateOf(
            TextInputState(
                label = "Aadhar number",
                inputConfig = InputConfig.fixedLengthNumber(12)
            )
        )
    }
    
    val panNoInput = remember {
        mutableStateOf(
            TextInputState(
                label = "PAN number",
                inputConfig = InputConfig.text {
                    maxLength = 10
                    strictMaxLengthCheck = true
                    regexValidation = InputConfig.RegexValidation(
                        Regex("^[A-Z]{5}\\d{4}[A-Z]{1}$")
                    )
                }
            )
        )
    }
  2. Composable

    TextInputLayout(state = nameInput)
    TextInputLayout(state = aadharNoInput)
    TextInputLayout(state = panNoInput)
  3. Validation

    if (
        TextInputState.allHaveValidInputs(
            nameInput, ageInput, contactNoInput, emailInput, aadharNoInput, panNoInput, passwordInput
        )
    ) {
        // Proceed...
    }


A dialog to input some text with strong validation mechanism.

TextInputDialogDemo.mp4
textInputDialogState.value = TextInputDialogState.Visible(
    title = "Enter your birth year",
    input = mutableStateOf(
        TextInputState(
            label = "Year",
            inputConfig = InputConfig.fixedLengthNumber(4)
        )
    ),
    submit = { input ->
        val birthYear = input.toInt() // No need to worry about error; already handled by TIL
        val age = Calendar.getInstance().get(YEAR) - birthYear
        showMessageDialog("Age", "Your age is $age years.")
    }
)


TextField & Dropdown that allows user to select one of multiple options.

val state = remember {
    mutableStateOf(
        TextInputState("Country")
    )
}

OutlinedSpinner(
    options = listOf("Bharat", "USA", "Russia", "China"),
    state = state,
    allowInput = true
)


Integrate Search functionality on your screen with minimal effort :

val searchAppBarState = rememberSearchAppBarState()

SearchAppBarScaffold(
    title = "Search AppBar Sample",
    navigateUp = { navController.navigateUp() },
    searchAppBarState = viewModel.searchAppBarState,
    onQueryChanged = viewModel::filter // <- Search event handler
) {
    // ... Content UI
}

// Inside ViewModel
fun filter(query: String) {
    // ... Filter the data and update state
}


Capturable is a composable that allows you to (capture / take snapshot of / take screenshot of) any composable.

val captureState = rememberCaptureState()

Capturable(state = captureState) {
   // Place anything you wanna capture
}

Button(
   onClick = {
      captureState.capture { bitmap ->
         saveAndShareImage(bitmap)
      }
   }
) {
   Text(text = "Capture & Share")
}


DrawingPad is a composable that allows user to draw on it.

val drawingPadState = rememberDrawingPadState()

DrawingPad(
   modifier = Modifier.fillMaxSize(),
   state = drawingPadState
)

FloatingActionButton(
   onClick = {
      drawingPadState.capture { bitmap ->
         saveAndShareImage(bitmap)
      }
   }
) {
   Icon(
      imageVector = Icons.Default.Share,
      contentDescription = "Capture & Share"
   )
}


TitleBarScaffold(
    title = "DroidLibs Sample",
    navigateUp = { navController.navigateUp() }
) { paddingValues ->
    // ... Content
}


val checked = remember { mutableStateOf(false) }

LabelledCheckBox(
    state = checked,
    label = "Gift wrap"
)


val gender = remember { mutableStateOf<String?>(null) }

RadioGroup(
    title = "Radio Group",
    state = gender,
    options = listOf("Male", "Female", "Other")
)


val gender by remember { mutableStateOf<String?>(null) }

LabelledRadioButton(
    label = "Male",
    selected = gender == "Male",
    onClick = { gender = "Male" }
)


BottomSheet(
    title = "Bottom Sheet Sample",
    state = bottomSheetState
) {
    // Content...
}



Center {
    Button(
        onClick = { navController.navigateUp() }
    ) {
        Text(text = "Go back")
    }
}

Text(
    modifier = Modifier
        .noRippleClickable {
            showToast("NO Ripple click")
        },
    text = "Without Ripple"
)