Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update value for TextField using StreamBuilder?

I have a textfield and i am using sqflite database in my app. The sqflite has a value which i need to assign to my textfield

Here is my textfield code

 StreamBuilder<String>(
    stream: patientHealthFormBloc.doctorName,
    builder: (context, snapshot) {
      return TextFormField(
        initialValue: patientHealthFormBloc.doctorNameValue,
        onChanged: (value) {
          patientHealthFormBloc.doctorNameChanged(value);
        },
        ...

Now in the initstate method of my class, i am fetching value from database. It being an asynchronous operation so it takes time.

My bloc class has a code like follows

Function(String) get doctorNameChanged => _doctorName.sink.add;

so as soon as i receive value from database i call following

doctorNameChanged("valuefromdatabase");

but i cant see the value in my textfield. Also there is a value present in my database. Is it possible to update the value without using TextEditingController or setState. I ma trying to avoid those as my class is divided in lot of chuncks and way too complicated to use any of the above I have tried using same approach with RadioButton and CheckBox and they seem to update properly. The value is also updated in _doctorName.stream.value which is present in the database but the textfield does not show any data. Also i tried changing color of textfield so there is no issue there as well as i am able to see what i type.

I have made a small demo of the app https://github.com/PritishSawant/demo/tree/master/lib

Instead of using sqflite, i am using shared preferences but the problem persists

like image 392
Pritish Avatar asked Jan 02 '20 11:01

Pritish


3 Answers

Try the following approach:

StreamBuilder<String>(
  stream: patientHealthFormBloc.doctorName,
  builder: (context, snapshot) {
    String doctorName = patientHealthFormBloc.doctorNameValue;
    if(snapshot.hasData){
      doctorName = snapshot.data/*(your name string from the stream)*/;
    } 
    return TextFormField(
      initialValue: doctorName,
      onChanged: (value) {
        patientHealthFormBloc.doctorNameChanged(value);
      },
    ...
like image 97
Harshvardhan Joshi Avatar answered Oct 19 '22 18:10

Harshvardhan Joshi


What was suggesting in my comments was something like this:

TextEditingController _textEditingController = TextEditingController();

@override
void initState() {
  patientHealthFormBloc.doctorName.listen((snapshot){
    setState((){
      _textEditingController.text = snapshot;
    });
  });
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: <Widget>[
      TextFormField(
        controller: _textEditingController,
        onChanged: (value) {
          patientHealthFormBloc.doctorNameChanged(value);
        },
      ],
    ),
  );
}

I didn't want to write this answer without understanding why you didn't want to use a TextEditingController or a setState. But this should achieve what you want while using the Bloc pattern.

like image 39
João Soares Avatar answered Oct 19 '22 20:10

João Soares


OK so i finally found the solution to my problem.

Following is my code, I have just used SharedPreferences instead of sqflite for the below example.Same thing can be done with sqflite

class _MyHomePageState extends State<MyHomePage> {

  MyBloc myBloc = MyBloc();
  TextEditingController myController = TextEditingController();

  @override
  void dispose() {
    myBloc?.close();
    myController?.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    AppPreferences.setString("data", "this is my data");
    AppPreferences.getString("data").then((value){
      myBloc.dataChanged(value);
    });
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: StreamBuilder(
          stream: myBloc.data,
           builder: (context,snapshot){

            debugPrint(snapshot.data);
            myController.value = myController.value.copyWith(text: myBloc.dataValue);

            return TextFormField(
              controller: myController,
              onChanged: (value){
                myBloc.dataChanged(value);
              },
            );
           },
        ),
      ),
    );
  }
}
like image 4
Pritish Avatar answered Oct 19 '22 18:10

Pritish