Jak zaktualizować wartość TextField za pomocą StreamBuilder?

13

Mam textfieldi używam sqflitebazy danych w mojej aplikacji. ThesqfliteMa wartość, którą muszę przypisać do mojegotextfield

Oto mój textfieldkod

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

Teraz w initstatemetodzie mojej klasy pobieram wartość z bazy danych. Jest to operacja asynchroniczna, więc wymaga czasu.

Moja klasa blokowa ma następujący kod

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

więc jak tylko otrzymam wartość z bazy danych, wywołuję następujące

doctorNameChanged("valuefromdatabase");

ale nie widzę wartości w moim polu tekstowym. W mojej bazie danych jest także wartość. Czy można zaktualizować wartość bez użycia TextEditingControllerlub setState. MA I starają się unikać tych, jak moja klasa jest podzielona na wiele chuncks i zbyt skomplikowane, aby użyć dowolnego z powyższych Próbowałem, używając tego samego podejścia z RadioButtona CheckBoxi wydają się poprawnie zaktualizować. Wartość jest również aktualizowana, w _doctorName.stream.valuektórej jest obecna w bazie danych, ale textfieldnie pokazuje żadnych danych. Próbowałem też zmienić kolortextfield więc nie ma problemu, a także mogę zobaczyć, co wpisuję.

Zrobiłem małe demo aplikacji https://github.com/PritishSawant/demo/tree/master/lib

Zamiast używać sqflite, używam, shared preferencesale problem nadal występuje

Szturchać
źródło
Spróbuj usunąć „initialValue: patientHealthFormBloc.doctorNameValue” i uruchom go w „initialData” z StreamBuilder
Stel
@Stel Dzięki, ale nie działa
Posuń
Możesz użyć TextEditingController (text
@ChinkySight Używam Stream, aby uniknąć TextEditingController. Aplikacja jest skomplikowana, a korzystanie z TextEditingController nie jest możliwe
Posuń
W twoim przykładzie wcale nie używasz migawki. Jakie dane chcesz wyodrębnić, aby użyć ich w TextFormField? Dlaczego nie możesz użyć TextEditingController?
João Soares

Odpowiedzi:

4

OK, więc w końcu znalazłem rozwiązanie mojego problemu.

Poniżej znajduje się mój kod, którego właśnie użyłem SharedPreferenceszamiast sqflitew poniższym przykładziesqflite

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);
              },
            );
           },
        ),
      ),
    );
  }
}
Szturchać
źródło
To wyjątkowo przeprojektowane rozwiązanie. Chciałbym, abyś wyjaśnił dokładnie, co dokładnie chciałeś osiągnąć, zamiast chcieć, abyśmy naprawili twoje konkretne rozwiązanie. Jak skomentowałem twoje pytanie, wydaje się, że chcesz dokonywać aktualizacji TextField w czasie rzeczywistym, które mogą być aktywnie używane przez użytkownika. To bardzo dziwne i prawdopodobnie złe UX.
João Soares,
@ JoãoSoares Czy możesz opublikować swoje rozwiązanie. Z przyjemnością przyznam nagrodę i zaakceptuję twoje rozwiązanie, jeśli jest lepsze niż moje. Właśnie opublikowałem łatwiejsze rozwiązanie, aby każdy mógł to zrozumieć, ale w rzeczywistości moja aplikacja wymaga synchronizacji z sqflite oraz firestore, aby wyglądało na to, że zostałeś zaprojektowany
Nudge
Jeśli możesz wyjaśnić, dlaczego chcesz przesyłać strumieniowo (aktualizować) wartość TextFieldForm w tym samym czasie, gdy użytkownik może go używać, być może będę w stanie ci pomóc. Synchronizacja z SQFlite i Firestore nie ma wpływu na moje twierdzenie, że przedstawione rozwiązanie jest zbyt zaawansowane.
João Soares
@ JoãoSoares Przeczytaj ponownie pytanie.
Posuń
Przeczytałem już to pytanie wiele razy i widziałem kod udostępniony w GitHub. Twoje wyjaśnienie mówi o tym, co chcesz zrobić i jak chcesz napisać kod. Nie wyjaśnia, dlaczego chcesz, aby TextFormField był wypełniony w ten sposób Streaming danych lub przesłanką takiego zachowania.
João Soares
2

Wypróbuj następujące podejście:

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);
      },
    ...

Daj mi znać jeśli będziesz jeszcze potrzebować pomocy.

Harshvardhan Joshi
źródło
nie działa ...
Posuń
otrzymujesz nowe nazwiska w strumieniu?
Harshvardhan Joshi
tak, ale wartość nie jest aktualizowana
Posuń
Czy otrzymujesz te same nowe nazwiska builder: (context, snapshot)?
Harshvardhan Joshi
Kiedy piszę tekst, otrzymuję wpisany tekst. Na początkowym etapie pobierania z bazy danych drukuje wartość db. StreamBuilder działa dobrze, a wartość aktualizuje się, gdy ręcznie wpisuję, ale nie aktualizuje się, gdy jest pobierana z bazy danych
Posuń
1

W moich komentarzach sugerowałem coś takiego:

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);
        },
      ],
    ),
  );
}

Nie chciałem pisać tej odpowiedzi, nie rozumiejąc, dlaczego nie chcesz używać TextEditingController lub setState. Ale to powinno osiągnąć to, co chcesz, korzystając ze wzoru Bloc.

João Soares
źródło
Miałem tę myśl na początku, ale ponieważ cytowane pytanie i @Nudge nalegały na nieużywanie TextEditController, więc zeskrobałem ten pomysł. To wspaniałe, że tylko przy użyciu kontrolera rozwiązanie staje się proste i małe, aby działało dokładnie. Dobra robota.
Harshvardhan Joshi
1
Dziękuję, doceniam to. Niestety użytkownik wydawał się nieugięty w przestrzeganiu własnego pomysłu i nie próbował pisać właściwego rozwiązania, dlatego postanowili nie przypisywać poprawnej odpowiedzi ani nagrody.
João Soares
W porządku Nieważne więc.
Harshvardhan Joshi