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.
-
Add
Jitpack
to yoursetting.gradle.kts
:dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven("https://jitpack.io") // <- Add this } }
-
Add dependencies of required modules in your
build.gradle.kts
: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>") }
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'
}
}
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 consists of several commonly used Composables :
TextInputLayout is an advanced TextField with several validation options :
TextInputLayoutDemo.mp4
-
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}$") ) } ) ) }
-
Composable
TextInputLayout(state = nameInput) TextInputLayout(state = aadharNoInput) TextInputLayout(state = panNoInput)
-
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"
)