এই পৃষ্ঠাটি কীভাবে মান-ভিত্তিক TextField গুলি রাজ্য-ভিত্তিক TextField গুলিতে স্থানান্তর করতে পারেন তার উদাহরণ প্রদান করে। মান এবং রাজ্য-ভিত্তিক TextField গুলির মধ্যে পার্থক্য সম্পর্কে তথ্যের জন্য পাঠ্য ক্ষেত্রগুলি কনফিগার করুন পৃষ্ঠাটি দেখুন।
মৌলিক ব্যবহার
মূল্য-ভিত্তিক
@Composable fun OldSimpleTextField() { var state by rememberSaveable { mutableStateOf("") } TextField( value = state, onValueChange = { state = it }, singleLine = true, ) }
রাজ্য-ভিত্তিক
@Composable fun NewSimpleTextField() { TextField( state = rememberTextFieldState(), lineLimits = TextFieldLineLimits.SingleLine ) }
-
value, onValueChangeপ্রতিস্থাপন করুন এবংremember { mutableStateOf("")}rememberTextFieldState()দিয়ে মনে রাখুন। -
singleLine = truelineLimits = TextFieldLineLimits.SingleLineদিয়ে প্রতিস্থাপন করুন।
onValueChange এর মাধ্যমে ফিল্টার করা হচ্ছে
মূল্য-ভিত্তিক
@Composable fun OldNoLeadingZeroes() { var input by rememberSaveable { mutableStateOf("") } TextField( value = input, onValueChange = { newText -> input = newText.trimStart { it == '0' } } ) }
রাজ্য-ভিত্তিক
@Preview @Composable fun NewNoLeadingZeros() { TextField( state = rememberTextFieldState(), inputTransformation = InputTransformation { while (length > 0 && charAt(0) == '0') delete(0, 1) } ) }
- কলব্যাক লুপের মান
rememberTextFieldState()দিয়ে প্রতিস্থাপন করুন। -
InputTransformationব্যবহার করেonValueChangeএ ফিল্টারিং লজিক পুনরায় প্রয়োগ করুন। -
stateআপডেট করতেInputTransformationএর রিসিভার স্কোপ থেকেTextFieldBufferব্যবহার করুন।- ব্যবহারকারীর ইনপুট সনাক্ত হওয়ার ঠিক পরেই
InputTransformationবলা হয়। -
TextFieldBufferএর মাধ্যমেInputTransformationদ্বারা প্রস্তাবিত পরিবর্তনগুলি অবিলম্বে প্রয়োগ করা হয়, সফ্টওয়্যার কীবোর্ড এবংTextFieldমধ্যে সিঙ্ক্রোনাইজেশন সমস্যা এড়ানো যায়।
- ব্যবহারকারীর ইনপুট সনাক্ত হওয়ার ঠিক পরেই
ক্রেডিট কার্ড ফর্ম্যাটার TextField
মূল্য-ভিত্তিক
@Composable fun OldTextFieldCreditCardFormatter() { var state by remember { mutableStateOf("") } TextField( value = state, onValueChange = { if (it.length <= 16) state = it }, visualTransformation = VisualTransformation { text -> // Making XXXX-XXXX-XXXX-XXXX string. var out = "" for (i in text.indices) { out += text[i] if (i % 4 == 3 && i != 15) out += "-" } TransformedText( text = AnnotatedString(out), offsetMapping = object : OffsetMapping { override fun originalToTransformed(offset: Int): Int { if (offset <= 3) return offset if (offset <= 7) return offset + 1 if (offset <= 11) return offset + 2 if (offset <= 16) return offset + 3 return 19 } override fun transformedToOriginal(offset: Int): Int { if (offset <= 4) return offset if (offset <= 9) return offset - 1 if (offset <= 14) return offset - 2 if (offset <= 19) return offset - 3 return 16 } } ) } ) }
রাজ্য-ভিত্তিক
@Composable fun NewTextFieldCreditCardFormatter() { val state = rememberTextFieldState() TextField( state = state, inputTransformation = InputTransformation.maxLength(16), outputTransformation = OutputTransformation { if (length > 4) insert(4, "-") if (length > 9) insert(9, "-") if (length > 14) insert(14, "-") }, ) }
- ইনপুটের সর্বোচ্চ দৈর্ঘ্য নির্ধারণ করতে
onValueChangeএর ফিল্টারিংটিInputTransformationদিয়ে প্রতিস্থাপন করুন।-
onValueChangeমাধ্যমে ফিল্টারিং" বিভাগটি দেখুন।
-
- ড্যাশ যোগ করতে
VisualTransformationএর পরিবর্তেOutputTransformationব্যবহার করুন।-
VisualTransformationএর মাধ্যমে, ড্যাশ দিয়ে নতুন টেক্সট তৈরি করা এবং ভিজ্যুয়াল টেক্সট এবং ব্যাকিং স্টেটের মধ্যে সূচকগুলি কীভাবে ম্যাপ করা হয়েছে তা গণনা করার জন্য আপনি দায়ী। -
OutputTransformationস্বয়ংক্রিয়ভাবে অফসেট ম্যাপিংয়ের যত্ন নেয়। আপনাকে কেবলOutputTransformation.transformOutputএর রিসিভার স্কোপ থেকেTextFieldBufferব্যবহার করে সঠিক স্থানে ড্যাশ যুক্ত করতে হবে।
-
অবস্থা আপডেট করা হচ্ছে (সহজ)
মূল্য-ভিত্তিক
@Composable fun OldTextFieldStateUpdate(userRepository: UserRepository) { var username by remember { mutableStateOf("") } LaunchedEffect(Unit) { username = userRepository.fetchUsername() } TextField( value = username, onValueChange = { username = it } ) }
রাজ্য-ভিত্তিক
@Composable fun NewTextFieldStateUpdate(userRepository: UserRepository) { val usernameState = rememberTextFieldState() LaunchedEffect(Unit) { usernameState.setTextAndPlaceCursorAtEnd(userRepository.fetchUsername()) } TextField(state = usernameState) }
- কলব্যাক লুপের মান
rememberTextFieldState()দিয়ে প্রতিস্থাপন করুন। -
TextFieldState.setTextAndPlaceCursorAtEndদিয়ে মান অ্যাসাইনমেন্ট পরিবর্তন করুন।
অবস্থা আপডেট করা হচ্ছে (জটিল)
মূল্য-ভিত্তিক
@Composable fun OldTextFieldAddMarkdownEmphasis() { var markdownState by remember { mutableStateOf(TextFieldValue()) } Button(onClick = { // add ** decorations around the current selection, also preserve the selection markdownState = with(markdownState) { copy( text = buildString { append(text.take(selection.min)) append("**") append(text.substring(selection)) append("**") append(text.drop(selection.max)) }, selection = TextRange(selection.min + 2, selection.max + 2) ) } }) { Text("Bold") } TextField( value = markdownState, onValueChange = { markdownState = it }, maxLines = 10 ) }
রাজ্য-ভিত্তিক
@Composable fun NewTextFieldAddMarkdownEmphasis() { val markdownState = rememberTextFieldState() LaunchedEffect(Unit) { // add ** decorations around the current selection markdownState.edit { insert(originalSelection.max, "**") insert(originalSelection.min, "**") selection = TextRange(originalSelection.min + 2, originalSelection.max + 2) } } TextField( state = markdownState, lineLimits = TextFieldLineLimits.MultiLine(1, 10) ) }
এই ব্যবহারের ক্ষেত্রে, একটি বোতাম মার্কডাউন সজ্জা যোগ করে কার্সার বা বর্তমান নির্বাচনের চারপাশে টেক্সটটিকে বোল্ড করে। এটি পরিবর্তনের পরে নির্বাচনের অবস্থানও বজায় রাখে।
- কলব্যাক লুপের মান
rememberTextFieldState()দিয়ে প্রতিস্থাপন করুন। -
maxLines = 10lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 10)দিয়ে প্রতিস্থাপন করুন। - একটি নতুন
TextFieldValueগণনার যুক্তিTextFieldState.editকলের মাধ্যমে পরিবর্তন করুন।- বর্তমান নির্বাচনের উপর ভিত্তি করে বিদ্যমান টেক্সটকে আলাদা করে এবং এর মধ্যে মার্কডাউন ডেকোরেশন সন্নিবেশ করে একটি নতুন
TextFieldValueতৈরি করা হয়। - এছাড়াও পাঠ্যের নতুন সূচক অনুসারে নির্বাচনটি সমন্বয় করা হয়।
-
TextFieldState.editTextFieldBufferব্যবহার করে বর্তমান অবস্থা সম্পাদনা করার একটি আরও স্বাভাবিক উপায় রয়েছে। - নির্বাচনটি স্পষ্টভাবে সংজ্ঞায়িত করে যে কোথায় সাজসজ্জা ঢোকাতে হবে।
- তারপর,
onValueChangeপদ্ধতির মতো নির্বাচনটি সামঞ্জস্য করুন।
- বর্তমান নির্বাচনের উপর ভিত্তি করে বিদ্যমান টেক্সটকে আলাদা করে এবং এর মধ্যে মার্কডাউন ডেকোরেশন সন্নিবেশ করে একটি নতুন
ভিউমডেল StateFlow আর্কিটেকচার
অনেক অ্যাপ্লিকেশন আধুনিক অ্যাপ ডেভেলপমেন্ট নির্দেশিকা অনুসরণ করে, যা সমস্ত তথ্য বহনকারী একটি একক অপরিবর্তনীয় শ্রেণীর মাধ্যমে একটি স্ক্রিন বা উপাদানের UI অবস্থা সংজ্ঞায়িত করার জন্য একটি StateFlow ব্যবহারকে উৎসাহিত করে।
এই ধরণের অ্যাপ্লিকেশনগুলিতে, টেক্সট ইনপুট সহ লগইন স্ক্রিনের মতো একটি ফর্ম সাধারণত নিম্নরূপ ডিজাইন করা হয়:
class LoginViewModel : ViewModel() { private val _uiState = MutableStateFlow(UiState()) val uiState: StateFlow<UiState> get() = _uiState.asStateFlow() fun updateUsername(username: String) = _uiState.update { it.copy(username = username) } fun updatePassword(password: String) = _uiState.update { it.copy(password = password) } } data class UiState( val username: String = "", val password: String = "" ) @Composable fun LoginForm( loginViewModel: LoginViewModel, modifier: Modifier = Modifier ) { val uiState by loginViewModel.uiState.collectAsStateWithLifecycle() Column(modifier) { TextField( value = uiState.username, onValueChange = { loginViewModel.updateUsername(it) } ) TextField( value = uiState.password, onValueChange = { loginViewModel.updatePassword(it) }, visualTransformation = PasswordVisualTransformation() ) } }
এই নকশাটি TextFields সাথে পুরোপুরি মানানসই value, onValueChange স্টেট হোস্টিং প্যারাডাইম ব্যবহার করে। তবে, টেক্সট ইনপুটের ক্ষেত্রে এই পদ্ধতির কিছু অপ্রত্যাশিত অসুবিধা রয়েছে। এই পদ্ধতির সাথে গভীর সিঙ্ক্রোনাইজেশন সমস্যাগুলি Effective state management for TextField in Compose ব্লগ পোস্টে বিস্তারিতভাবে ব্যাখ্যা করা হয়েছে।
সমস্যা হলো নতুন TextFieldState ডিজাইনটি StateFlow সমর্থিত ViewModel UI অবস্থার সাথে সরাসরি সামঞ্জস্যপূর্ণ নয়। username: String এবং password: String কে username: TextFieldState এবং password: TextFieldState দিয়ে প্রতিস্থাপন করা অদ্ভুত লাগতে পারে, কারণ TextFieldState একটি সহজাতভাবে পরিবর্তনযোগ্য ডেটা কাঠামো।
একটি সাধারণ সুপারিশ হল ViewModel এ UI নির্ভরতা রাখা এড়িয়ে চলা। যদিও এটি সাধারণত একটি ভালো অনুশীলন, তবে কখনও কখনও এটির ভুল ব্যাখ্যা করা যেতে পারে। এটি বিশেষ করে Compose নির্ভরতার ক্ষেত্রে সত্য যা সম্পূর্ণরূপে ডেটা স্ট্রাকচার এবং TextFieldState মতো কোনও UI উপাদান বহন করে না।
MutableState অথবা TextFieldState মতো ক্লাসগুলি হল সরল স্টেট হোল্ডার যা Compose এর Snapshot state সিস্টেম দ্বারা সমর্থিত। এগুলি StateFlow অথবা RxJava এর মতো নির্ভরশীলতা থেকে আলাদা নয়। অতএব, আমরা আপনাকে আপনার কোডে "no UI dependences in ViewModel" নীতিটি কীভাবে প্রয়োগ করবেন তা পুনর্মূল্যায়ন করতে উৎসাহিত করছি। আপনার ViewModel এর মধ্যে TextFieldState এর রেফারেন্স রাখা সহজাতভাবে খারাপ অভ্যাস নয়।
প্রস্তাবিত সহজ পদ্ধতি
আমরা আপনাকে UiState থেকে username বা password মতো মানগুলি বের করার এবং ViewModel এ তাদের জন্য একটি পৃথক রেফারেন্স রাখার পরামর্শ দিচ্ছি।
class LoginViewModel : ViewModel() { val usernameState = TextFieldState() val passwordState = TextFieldState() } @Composable fun LoginForm( loginViewModel: LoginViewModel, modifier: Modifier = Modifier ) { Column(modifier) { TextField(state = loginViewModel.usernameState,) SecureTextField(state = loginViewModel.passwordState) } }
-
MutableStateFlow<UiState>এর পরিবর্তে কয়েকটিTextFieldStateমান ব্যবহার করুন। -
LoginFormকম্পোজেবলেরTextFieldStateঅবজেক্টগুলিকেTextFieldsএ পাস করুন।
সঙ্গতিপূর্ণ পদ্ধতি
এই ধরণের স্থাপত্য পরিবর্তনগুলি সবসময় সহজ হয় না। আপনার এই পরিবর্তনগুলি করার স্বাধীনতা নাও থাকতে পারে, অথবা নতুন TextField ব্যবহারের সুবিধার চেয়ে সময় বিনিয়োগ বেশি হতে পারে। এই ক্ষেত্রে, আপনি এখনও কিছুটা পরিবর্তন করে রাজ্য-ভিত্তিক টেক্সট ফিল্ড ব্যবহার করতে পারেন।
class LoginViewModel : ViewModel() { private val _uiState = MutableStateFlow(UiState()) val uiState: StateFlow<UiState> get() = _uiState.asStateFlow() fun updateUsername(username: String) = _uiState.update { it.copy(username = username) } fun updatePassword(password: String) = _uiState.update { it.copy(password = password) } } data class UiState( val username: String = "", val password: String = "" ) @Composable fun LoginForm( loginViewModel: LoginViewModel, modifier: Modifier = Modifier ) { val initialUiState = remember(loginViewModel) { loginViewModel.uiState.value } Column(modifier) { val usernameState = rememberTextFieldState(initialUiState.username) LaunchedEffect(usernameState) { snapshotFlow { usernameState.text.toString() }.collectLatest { loginViewModel.updateUsername(it) } } TextField(usernameState) val passwordState = rememberTextFieldState(initialUiState.password) LaunchedEffect(usernameState) { snapshotFlow { usernameState.text.toString() }.collectLatest { loginViewModel.updatePassword(it) } } SecureTextField(passwordState) } }
- তোমার
ViewModelএবংUiStateক্লাস একই রাখো। -
ViewModelএ সরাসরি অবস্থা তুলে ধরার পরিবর্তে এবং এটিকেTextFieldsজন্য সত্যের উৎস করার পরিবর্তে,ViewModelএকটি সাধারণ ডেটা হোল্ডারে পরিণত করুন।- এটি করার জন্য,
LaunchedEffectএ একটিsnapshotFlowসংগ্রহ করে প্রতিটিTextFieldState.textএর পরিবর্তনগুলি পর্যবেক্ষণ করুন।
- এটি করার জন্য,
- আপনার
ViewModelএখনও UI থেকে সর্বশেষ মান থাকবে, কিন্তু এরuiState: StateFlow<UiState>TextFieldগুলি চালাবে না। - আপনার
ViewModelএ প্রয়োগ করা অন্য যেকোনো স্থায়িত্ব যুক্তি একই থাকতে পারে।