এই পৃষ্ঠাটি উদাহরণ প্রদান করে যে আপনি কীভাবে মান-ভিত্তিক 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 = true
দিয়েlineLimits = 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, "-") }, ) }
- ইনপুটের সর্বোচ্চ দৈর্ঘ্য সেট করতে একটি
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 = 10
lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 10)
দিয়ে প্রতিস্থাপন করুন। - একটি
TextFieldState.edit
কল দিয়ে একটি নতুনTextFieldValue
গণনার যুক্তি পরিবর্তন করুন।- বর্তমান নির্বাচনের উপর ভিত্তি করে বিদ্যমান পাঠ্যকে বিভক্ত করে এবং এর মধ্যে মার্কডাউন সজ্জা সন্নিবেশ করে একটি নতুন
TextFieldValue
তৈরি করা হয়। - এছাড়াও নির্বাচন পাঠ্যের নতুন সূচক অনুযায়ী সামঞ্জস্য করা হয়।
-
TextFieldState.edit
TextFieldBuffer
ব্যবহার করে বর্তমান অবস্থা সম্পাদনা করার আরও স্বাভাবিক উপায় রয়েছে। - নির্বাচন স্পষ্টভাবে সংজ্ঞায়িত করে যেখানে সজ্জা সন্নিবেশ করতে হবে।
- তারপর,
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
স্টেট হোস্টিং প্যারাডাইম। যাইহোক, যখন টেক্সট ইনপুট আসে তখন এই পদ্ধতির অপ্রত্যাশিত খারাপ দিক রয়েছে। এই পদ্ধতির সাথে গভীর সিঙ্ক্রোনাইজেশন সমস্যাগুলি কম্পোজ ব্লগ পোস্টে টেক্সটফিল্ডের জন্য কার্যকরী রাষ্ট্র পরিচালনায় বিশদভাবে ব্যাখ্যা করা হয়েছে।
সমস্যা হল নতুন TextFieldState
ডিজাইন StateFlow
ব্যাকড ViewModel UI স্টেটের সাথে সরাসরি সামঞ্জস্যপূর্ণ নয়। username: String
এবং password: String
username: TextFieldState
এবং password: TextFieldState
, যেহেতু TextFieldState
একটি অন্তর্নিহিত পরিবর্তনযোগ্য ডেটা কাঠামো।
একটি সাধারণ সুপারিশ হল একটি ViewModel
এ UI নির্ভরতা স্থাপন করা এড়ানো। যদিও এটি সাধারণত একটি ভাল অভ্যাস, তবে এটি কখনও কখনও ভুল ব্যাখ্যা হতে পারে। এটি রচনা নির্ভরতার জন্য বিশেষভাবে সত্য যা সম্পূর্ণরূপে ডেটা স্ট্রাকচার এবং তাদের সাথে কোন UI উপাদান বহন করে না, যেমন TextFieldState
।
MutableState
বা TextFieldState
মতো ক্লাসগুলি হল সাধারণ স্টেট হোল্ডার যেগুলি কম্পোজের স্ন্যাপশট স্টেট সিস্টেম দ্বারা সমর্থিত৷ তারা StateFlow
বা RxJava
মত নির্ভরতা থেকে আলাদা নয়। অতএব, আমরা আপনাকে আপনার কোডে "ভিউমডেলে কোন UI নির্ভরতা নেই" নীতি কীভাবে প্রয়োগ করেন তা পুনরায় মূল্যায়ন করতে উত্সাহিত করি৷ আপনার 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
s চালাবে না। - আপনার
ViewModel
প্রয়োগ করা অন্য যেকোন অধ্যবসায় যুক্তি একই থাকতে পারে।