Przekazywanie wartości do poprzedniego widżetu

Aug 16 2020

Mam prosty formularz, w środku mam CircularAvatar po naciśnięciu tego przycisku pokaż ModalBottomSheet, aby wybrać między robieniem zdjęcia z galerii lub aparatu. Aby mój widget był bardziej kompaktowy, oddzieliłem go na jakiś plik.

  1. FormDosenScreen (to główny ekran)
  2. DosenImagePicker (to tylko CircularAvatar)
  3. ModalBottomSheetPickImage (ma pokazywać ModalBottomSheet)

Problem polega na tym, że nie wiem, jak przekazać wartość z ModalBottomSheetPickImage do FormDosenScreen . Ponieważ wartość z ModalBottomSheetPickImage użyję do wstawienia operacji.

Pomyślnie przechodzę tylko z trzeciego widżetu do drugiego widżetu, ale kiedy przechodzę ponownie z drugiego widżetu do pierwszego widżetu, wartość jest zerowa i myślę, że problem przechodzi z drugiego widżetu do pierwszego widżetu.

Jak mogę przejść z trzeciego widżetu do pierwszego widżetu?

Pierwszy widżet

class FormDosenScreen extends StatefulWidget {
  static const routeNamed = '/formdosen-screen';

  @override
  _FormDosenScreenState createState() => _FormDosenScreenState();
}

class _FormDosenScreenState extends State<FormDosenScreen> {
  String selectedFile;
  @override
  Widget build(BuildContext context) {
    final detectKeyboardOpen = MediaQuery.of(context).viewInsets.bottom;
    print('trigger');
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text('Tambah Dosen'),
        actions: <Widget>[
          PopupMenuButton(
            itemBuilder: (_) => [
              PopupMenuItem(
                child: Text('Tambah Pelajaran'),
                value: 'add_pelajaran',
              ),
            ],
            onSelected: (String value) {
              switch (value) {
                case 'add_pelajaran':
                  Navigator.of(context).pushNamed(FormPelajaranScreen.routeNamed);
                  break;
                default:
              }
            },
          )
        ],
      ),
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          SingleChildScrollView(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                SizedBox(height: 20),
                DosenImagePicker(onPickedImage: (file) => selectedFile = file),
                SizedBox(height: 20),
                Card(
                  margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
                  child: Padding(
                    padding: const EdgeInsets.all(20),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      children: <Widget>[
                        TextFormFieldCustom(
                          onSaved: (value) {},
                          labelText: 'Nama Dosen',
                        ),
                        SizedBox(height: 20),
                        TextFormFieldCustom(
                          onSaved: (value) {},
                          prefixIcon: Icon(Icons.email),
                          labelText: 'Email Dosen',
                          keyboardType: TextInputType.emailAddress,
                        ),
                        SizedBox(height: 20),
                        TextFormFieldCustom(
                          onSaved: (value) {},
                          keyboardType: TextInputType.number,
                          inputFormatter: [
                            // InputNumberFormat(),
                            WhitelistingTextInputFormatter.digitsOnly
                          ],
                          prefixIcon: Icon(Icons.local_phone),
                          labelText: 'Telepon Dosen',
                        ),
                      ],
                    ),
                  ),
                ),
                SizedBox(height: kToolbarHeight),
              ],
            ),
          ),
          Positioned(
            child: Visibility(
              visible: detectKeyboardOpen > 0 ? false : true,
              child: RaisedButton(
                onPressed: () {
                  print(selectedFile);
                },
                materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
                color: colorPallete.primaryColor,
                child: Text(
                  'SIMPAN',
                  style: TextStyle(fontWeight: FontWeight.bold, fontFamily: AppConfig.headerFont),
                ),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
                textTheme: ButtonTextTheme.primary,
              ),
            ),
            bottom: kToolbarHeight / 2,
            left: sizes.width(context) / 15,
            right: sizes.width(context) / 15,
          )
        ],
      ),
    );
  }
}

Drugi widżet


class DosenImagePicker extends StatefulWidget {
  final Function(String file) onPickedImage;
  DosenImagePicker({@required this.onPickedImage});
  @override
  DosenImagePickerState createState() => DosenImagePickerState();
}

class DosenImagePickerState extends State<DosenImagePicker> {
  String selectedImage;
  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.center,
      child: InkWell(
        onTap: () async {
          await showModalBottomSheet(
            context: context,
            builder: (context) => ModalBottomSheetPickImage(
              onPickedImage: (file) {
                setState(() {
                  selectedImage = file;
                  widget.onPickedImage(selectedImage);
                  print('Hellooo dosen image picker $selectedImage');
                });
              },
            ),
          );
        },
        child: CircleAvatar(
          foregroundColor: colorPallete.black,
          backgroundImage: selectedImage == null ? null : MemoryImage(base64.decode(selectedImage)),
          radius: sizes.width(context) / 6,
          backgroundColor: colorPallete.accentColor,
          child: selectedImage == null ? Text('Pilih Gambar') : SizedBox(),
        ),
      ),
    );
  }
}

Trzeci widget


class ModalBottomSheetPickImage extends StatelessWidget {
  final Function(String file) onPickedImage;

  ModalBottomSheetPickImage({@required this.onPickedImage});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: Padding(
        padding: const EdgeInsets.all(15.0),
        child: Wrap(
          alignment: WrapAlignment.spaceEvenly,
          children: <Widget>[
            InkWell(
              onTap: () async {
                final String resultBase64 =
                    await commonFunction.pickImage(quality: 80, returnFile: ReturnFile.BASE64);
                onPickedImage(resultBase64);
              },
              child: CircleAvatar(
                foregroundColor: colorPallete.white,
                backgroundColor: colorPallete.green,
                child: Icon(Icons.camera_alt),
              ),
            ),
            InkWell(
              onTap: () async {
                final String resultBase64 =
                    await commonFunction.pickImage(returnFile: ReturnFile.BASE64, isCamera: false);
                onPickedImage(resultBase64);
              },
              child: CircleAvatar(
                foregroundColor: colorPallete.white,
                backgroundColor: colorPallete.blue,
                child: Icon(Icons.photo_library),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Odpowiedzi

2 Uni Aug 17 2020 at 04:30

Najczystszym i najłatwiejszym sposobem jest skorzystanie z Dostawcy. Jest to jedno z rozwiązań do zarządzania stanem, którego można używać do przekazywania wartości wokół aplikacji, a także do odbudowywania tylko zmienionych widżetów. (Np .: gdy zmienia się wartość widżetu Tekst). Oto, jak możesz użyć Provider w swoim scenariuszu:

Oto jak powinien wyglądać Twój model:

class ImageModel extends ChangeNotifier {
  String _base64Image;
  get base64Image => _base64Image;
  set base64Image(String base64Image) {
    _base64Image = base64Image;
    notifyListeners();
  }
}

Nie zapomnij dodać metod pobierających i ustawiających, dzięki czemu będziesz mógł używać notifyListeners (), jeśli masz zależny od tego interfejs użytkownika.

Oto jak możesz uzyskać dostęp do wartości ImageModel w swoim interfejsie użytkownika:

final model=Provider.of<ImageModel>(context,listen:false);
String image=model.base64Image; //get data
model.base64Image=resultBase64; //set your image data after you used ImagePicker

Oto, w jaki sposób możesz wyświetlić swoje dane w widżecie tekstowym (najlepiej byłoby użyć selektora zamiast konsumenta, aby widżet odbudowywał się tylko wtedy, gdy wartość nasłuchiwania zmieni się):

@override
Widget build(BuildContext context) {
 //other widgets
 Selector<ImageModel, String>(
  selector: (_, model) => model.base64Image,
  builder: (_, image, __) {
   return Text(image);
     },
   );
  }
 )
}
VasilKanev Aug 17 2020 at 09:45

Możesz to łatwo osiągnąć. Jeśli używasz Blocs.