이전 위젯에 값 전달
나는 간단한 형태를 가지고 있으며, 내부에 CircularAvatar를 누르면 갤러리 또는 카메라에서 사진을 찍기 위해 ModalBottomSheet를 표시합니다. 내 위젯을 더 컴팩트하게 만들기 위해 일부 파일로 분리했습니다.
- FormDosenScreen (메인 화면)
- DosenImagePicker (CircularAvatar 전용)
- ModalBottomSheetPickImage (ModalBottomSheet을 표시하기위한 것임)
문제는 ModalBottomSheetPickImage 에서 FormDosenScreen 으로 값을 전달하는 방법을 모르겠다 는 것 입니다. ModalBottomSheetPickImage의 값은 작업을 삽입하는 데 사용할 것입니다.
세 번째 위젯에서 두 번째 위젯으로 전달하는 데 성공했지만 두 번째 위젯에서 첫 번째 위젯으로 다시 전달하면 값이 null이고 문제가 두 번째 위젯에서 첫 번째 위젯으로 전달되는 것 같습니다.
세 번째 위젯에서 첫 번째 위젯으로 어떻게 전달할 수 있습니까?
첫 번째 위젯
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,
)
],
),
);
}
}
두 번째 위젯
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(),
),
),
);
}
}
세 번째 위젯
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),
),
),
],
),
),
);
}
}
답변
이를 수행하는 가장 깨끗하고 쉬운 방법은 제공자를 이용하는 것입니다. 변경된 위젯 만 다시 빌드하고 앱 주변에 값을 전달하는 데 사용할 수있는 상태 관리 솔루션 중 하나입니다. (예 : 텍스트 위젯의 값이 변경되는 경우). 시나리오에서 Provider를 사용하는 방법은 다음과 같습니다.
모델의 모습은 다음과 같습니다.
class ImageModel extends ChangeNotifier {
String _base64Image;
get base64Image => _base64Image;
set base64Image(String base64Image) {
_base64Image = base64Image;
notifyListeners();
}
}
의존하는 UI가있는 경우 notifyListeners ()를 사용할 수 있도록 getter 및 setter를 추가하는 것을 잊지 마십시오.
UI에서 ImageModel 값에 액세스하는 방법은 다음과 같습니다.
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
다음은 텍스트 위젯에 데이터를 표시하는 방법입니다 (이상적으로, 수신 대기하는 값이 변경되는 경우에만 위젯이 다시 빌드되도록 Consumer 대신 Selector를 사용해야합니다).
@override
Widget build(BuildContext context) {
//other widgets
Selector<ImageModel, String>(
selector: (_, model) => model.base64Image,
builder: (_, image, __) {
return Text(image);
},
);
}
)
}
이것을 쉽게 달성 할 수 있습니다. 블록을 사용하는 경우.