Hi I need some help with passing an image that is selected from my gallery to another screen.
When I go to the 'Pick Image' screen through the grid-tiled screen, I can select an image from my gallery app. As soon as I pick one, the image pops up on the 'Pick Image' screen and it should be passed to the previous screen(grid tiled) so the image can be displayed in a grid tile.
Anyone could handle this?
The relevant part of the code is right below.
Grid Tile screen
Widget build(BuildContext context) {
return GridView.count(crossAxisCount: 4,
children: List.generate(lastDay, (index){
return GridTile(
child: Card(
child: Column(
children: <Widget>[
Text('Day ' '$index'),
SizedBox(height: 20.0,),
IconButton(
icon: Icon(Icons.add),
onPressed: (){
Navigator.push(context,
MaterialPageRoute(builder: (context) => PickImage()));
},),
],
),
),
);
}),
);
}
Pick Image Screen
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class PickImage extends StatefulWidget {
PickImage() : super();
final String title = "Pick Image";
#override
_PickImageState createState() => _PickImageState();
}
class _PickImageState extends State<PickImage> {
Future<File> imageFile;
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
width: 400,
height: 400,
);
} else if (snapshot.error != null) {
return const Text(
'Error Picking Image',
textAlign: TextAlign.center,
);
} else {
return const Text(
'No Image Selected',
textAlign: TextAlign.center,
);
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
showImage(),
RaisedButton(
child: Text("Select Image from Gallery"),
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
},
),
],
),
),
);
}
}
You can copy paste run full code below
Step 1: Use Navigator.pop to return image
RaisedButton(
onPressed: () {
Navigator.pop(context, imageFileReturn);
},
child: Text('Selecct Finish, Go back '),
),
Step 2: Use Map<int, File> keep related index and image
Map<int, File> imageFileMap = {};
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
imageFile = await Navigator.push(context,
MaterialPageRoute(builder: (context) => PickImage()));
imageFileMap[index] = imageFile;
setState(() {});
},
Step 3: Show image in Map
SizedBox(
height: 20.0,
child: imageFileMap[index] != null
? Image.file(
imageFileMap[index],
)
: Container()),
working demo
full code
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class PickImage extends StatefulWidget {
PickImage() : super();
final String title = "Pick Image";
#override
_PickImageState createState() => _PickImageState();
}
class _PickImageState extends State<PickImage> {
Future<File> imageFile;
File imageFileReturn;
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
imageFileReturn = snapshot.data;
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
width: 400,
height: 400,
);
} else if (snapshot.error != null) {
return const Text(
'Error Picking Image',
textAlign: TextAlign.center,
);
} else {
return const Text(
'No Image Selected',
textAlign: TextAlign.center,
);
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
showImage(),
RaisedButton(
child: Text("Select Image from Gallery"),
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
},
),
RaisedButton(
onPressed: () {
Navigator.pop(context, imageFileReturn);
},
child: Text('Selecct Finish, Go back '),
),
],
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Test(),
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
int lastDay = 30;
Map<int, File> imageFileMap = {};
File imageFile;
#override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 4,
children: List.generate(lastDay, (index) {
return GridTile(
child: Card(
child: Column(
children: <Widget>[
Text('Day ' '$index'),
SizedBox(
height: 20.0,
child: imageFileMap[index] != null
? Image.file(
imageFileMap[index],
)
: Container()),
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
imageFile = await Navigator.push(context,
MaterialPageRoute(builder: (context) => PickImage()));
imageFileMap[index] = imageFile;
setState(() {});
},
),
],
),
),
);
}),
);
}
}
Related
I am currently displaying my icons like this:
Widget _buildPopupDialog(BuildContext context) {
List<IconData> _iconsTable = [
Icons.feedback,
Icons.eco,
Icons.support,
Icons.call,
Icons.nature_people,
Icons.directions_bike,
];
return new AlertDialog(
content: SingleChildScrollView(
child: new Container(
child: GridView.count(
children: new List.generate(6, (int index) {
return new Positioned(
child: new DailyButton(iconData: _iconsTable[index]),
);
}),
),
),
),
However, I am wanting to get the icon data from cloud firestore. I am very new to using both flutter and firebase so I am very unsure how I would be able to do this. So far, I have tried this but iconData: Icons.iconsData obviously doesnt work:
class MyApp3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage3(),
);
}
}
class MyHomePage3 extends StatefulWidget {
#override
_MyHomePageState3 createState() {
return _MyHomePageState3();
}
}
class _MyHomePageState3 extends State<MyHomePage3> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('icons').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.docs);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
var _iconsData = record3.name;
return Padding(
key: ValueKey(record3.name),
child: Container(
child: Card(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: Icons.iconsData,
),
// trailing: Text(record3.votes.toString()),
// onTap: () => record3.reference.update({'votes': record3.votes+1})
),
),
);
}
}
class Record3 {
final String name;
final int votes;
final DocumentReference reference;
Record3.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
assert(map['votes'] != null),
name = map['name'],
votes = map['votes'];
Record3.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data(), reference: snapshot.reference);
#override
String toString() => "Record<$name:$votes>";
}
Any help would be greatly appreciated!
If anyone is interested, I was able to figure it out:
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
int iconCode = record3.votes;
return Padding(
key: ValueKey(record3.name),
child: Container(
child: new Container(
child: new ListView(
scrollDirection: Axis.horizontal,
children: new List.generate(1, (int index) {
return new Positioned(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: (IconData(iconCode, fontFamily: 'MaterialIcons')),
),
);
})),
),
),
);
I would like to create my own QR Code, Print it and whenever I want to scan it with my flutter app, it should redirect me to a screen of the app.
Is this possible?
import the qr_flutter package on pub.dev, this is the code to use below
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
void main() => runApp(MaterialApp(home: MyHome()));
class MyHome extends StatelessWidget {
const MyHome({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Flutter Demo Home Page')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => QRViewExample(),
));
},
child: Text('qrView'),
),
),
);
}
}
class QRViewExample extends StatefulWidget {
#override
State<StatefulWidget> createState() => _QRViewExampleState();
}
class _QRViewExampleState extends State<QRViewExample> {
Barcode? result;
QRViewController? controller;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
#override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
controller!.pauseCamera();
}
controller!.resumeCamera();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(flex: 4, child: _buildQrView(context)),
Expanded(
flex: 1,
child: FittedBox(
fit: BoxFit.contain,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (result != null)
Text(
'Barcode Type: ${describeEnum(result!.format)} Data: ${result!.code}')
else
Text('Scan a code'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.toggleFlash();
setState(() {});
},
child: FutureBuilder(
future: controller?.getFlashStatus(),
builder: (context, snapshot) {
return Text('Flash: ${snapshot.data}');
},
)),
),
Container(
margin: EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.flipCamera();
setState(() {});
},
child: FutureBuilder(
future: controller?.getCameraInfo(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return Text(
'Camera facing ${describeEnum(snapshot.data!)}');
} else {
return Text('loading');
}
},
)),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.pauseCamera();
},
child: Text('pause', style: TextStyle(fontSize: 20)),
),
),
Container(
margin: EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.resumeCamera();
},
child: Text('resume', style: TextStyle(fontSize: 20)),
),
)
],
),
],
),
),
)
],
),
);
}
Widget _buildQrView(BuildContext context) {
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
var scanArea = (MediaQuery.of(context).size.width < 400 ||
MediaQuery.of(context).size.height < 400)
? 150.0
: 300.0;
// To ensure the Scanner view is properly sizes after rotation
// we need to listen for Flutter SizeChanged notification and update controller
return QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
);
}
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
});
});
}
void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('no Permission')),
);
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
}
Use the qr_flutter package on pub.dev.
I have image shown using one of Flutter widgets
Image.network(....);
I want to add functionality that after tapping on image I can present this image in fullscreen mode. How it can be done?
You say you want something like in this flutter cookbook?
import 'package:flutter/material.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image/Detail Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
You can use the cache_network_image package to show the cached image without download it again.
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image/Detail Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: CachedNetworkImage(
imageUrl: 'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
)
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: CachedNetworkImage(
imageUrl: 'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
My final release (with real fullscreen):
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/services.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image/Detail Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: CachedNetworkImage(
imageUrl:
'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
)),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatefulWidget {
#override
_DetailScreenState createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
#override
initState() {
SystemChrome.setEnabledSystemUIOverlays([]);
super.initState();
}
#override
void dispose() {
//SystemChrome.restoreSystemUIOverlays();
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: CachedNetworkImage(
imageUrl:
'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
Passing data from main to detail page
Just to complete my answer, I add some code showing how you could pass the image url from main to detail page.
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/services.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image/Detail Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
var _url = [
'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
'https://github.com/flutter/plugins/raw/master/packages/video_player/doc/demo_ipod.gif?raw=true'
];
var _tag = ['imageHero', 'imageHero2'];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: ListView(
children: <Widget>[
GestureDetector(
child: Hero(
tag: _tag[0],
child: CachedNetworkImage(
imageUrl: _url[0],
placeholder: Center(child: Container(width: 32, height: 32,child: new CircularProgressIndicator())),
errorWidget: new Icon(Icons.error),
)),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen(tag: _tag[0], url: _url[0]);
}));
},
),
GestureDetector(
child: Hero(
tag: _tag[1],
child: CachedNetworkImage(
imageUrl: _url[1],
placeholder: Center(child: Container(width: 32, height: 32,child: new CircularProgressIndicator())),
errorWidget: new Icon(Icons.error),
)),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen(tag: _tag[1], url: _url[1]);
}));
},
),
],
),
);
}
}
class DetailScreen extends StatefulWidget {
final String tag;
final String url;
DetailScreen({Key key, #required this.tag, #required this.url})
: assert(tag != null),
assert(url != null),
super(key: key);
#override
_DetailScreenState createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
#override
initState() {
SystemChrome.setEnabledSystemUIOverlays([]);
super.initState();
}
#override
void dispose() {
//SystemChrome.restoreSystemUIOverlays();
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: widget.tag,
child: CachedNetworkImage(
imageUrl: widget.url,
placeholder: Center(child: Container(width: 32, height: 32,child: new CircularProgressIndicator())),
errorWidget: new Icon(Icons.error),
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
UPDATE
In order to pop back tapping outside the image, bring outside the GestureDetector in the Detail widget.
class _DetailScreenState extends State<DetailScreen> {
#override
initState() {
SystemChrome.setEnabledSystemUIOverlays([]);
super.initState();
}
#override
void dispose() {
//SystemChrome.restoreSystemUIOverlays();
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Scaffold (
body: Center(
child: Hero(
tag: widget.tag,
child: CachedNetworkImage(
imageUrl: widget.url,
placeholder: Center(child: Container(width: 32, height: 32,child: new CircularProgressIndicator())),
errorWidget: new Icon(Icons.error),
),
),
),
),
onTap: () {
Navigator.pop(context);
},
);
}
}
You can use this code to have full screen of image!
class DetailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
You can easily use this package easy_image_viewer
It worked perfectly without having to create a new screen to handle full screen.
The package handles full screen, pinching, zooming in and out of image.
Check this sample code below.
GestureDetector(
onTap: () {
showImageViewer(context, Image.asset("asset/image/myimage.jpg").image,
swipeDismissible: false);
},
child: Container(
height: 150,
child: Image.asset(
"asset/image/myimage.jpg",
fit: BoxFit.cover,
),
),
)
The Image opens in full screen, pinchable and zoomable.
I simply found a library with just about what you are looking for and with animated Hero effects.
full_screen_image by furkan.kayali#bil.omu.edu.tr
FullScreenWidget(
child: Hero(
tag: "customTag",
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.asset(
"assets/image2.jpg",
fit: BoxFit.cover,
),
),
),
);
Use easy_image_viewer package. view
This example for Network image,
This package provides an easy-to-use and highly customizable image viewer. It allows you to display images in a full-screen view with zooming, panning, and rotation capabilities.
#Amfstacks shows how this works for image which is in your assets. This example for Network image,
GestureDetector(
onTap: () async {
final imageProvider =
Image.network("your url").image;
showImageViewer(context, imageProvider,
onViewerDismissed: () {
print("dismissed");
});
},
child: Container(
height: 150,
child: Image(
image: NetworkImage("your url"),
)),
),
I am showing a dialog to the user and I want to change title of the dialog when dialog's positive button is clicked but how can I do that.
Since the dialog is already visible on the screen and setState() won't be able to do anything.
Code:
String title = "Old Title" // member variable
RaisedButton(onPressed: (){
showDialog(context: context, builder: (context) {
return AlertDialog(title: Text(title), actions: <Widget>[FlatButton(onPressed: () {
setState(() => title = "New Title");
}, child: Text("Change"))],);
});
}, child: Text("Change"),)
You can do that - without creating additional widget
RaisedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
StreamController<String> controller = StreamController<String>.broadcast();
return AlertDialog(
title: StreamBuilder(
stream: controller.stream,
builder: (BuildContext context, AsyncSnapshot<String> snapshot){
return Text(snapshot.hasData ? snapshot.data : 'Title');
}),
actions: [
FlatButton(
onPressed: () {
controller.add('New Title');
},
child: Text('Change'))
],
);
});
},
child: Text('Change'),
);
You can create a StatefulWidget that returns the AlertDialog. When you tap the change button, setState will update the text value of the dialog.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Dynamic Dialog',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Dynamic Dialog'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Press the FAB to present the dialog!',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: ((BuildContext context) {
return DynamicDialog(title: 'Original Title');
}));
},
tooltip: 'Show Dialog',
child: Icon(Icons.add),
),
);
}
}
class DynamicDialog extends StatefulWidget {
DynamicDialog({this.title});
final String title;
#override
_DynamicDialogState createState() => _DynamicDialogState();
}
class _DynamicDialogState extends State<DynamicDialog> {
String _title;
#override
void initState() {
_title = widget.title;
super.initState();
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(_title),
actions: <Widget>[
FlatButton(
onPressed: () {
final newText = 'Updated Title!';
setState(() {
_title = newText;
});
},
child: Text('Change'))
],
);
}
}
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text(title),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() => title = "New Title");
},
child: Text("Change"))
],
);
},
);
},
);
Say we created a Chip object and TextField object like below. How do you add a Chip to the inside of the TextField?
new Chip(
label: new Text('Peyton Smith'),
)
new TextField(
)
Is it possible to combine them to get something like in the Material spec where typing in something into a Material TextField adds a Chip?
What you are looking for was actually provided in the comment.
Here is a simple implementation using InputChip:
InputChip(
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text('AB'),
),
label: Text('Aaron Burr'),
onPressed: () {
print('I am the one thing in life.');
}
)
A material design input chip.
Input chips represent a complex piece of information, such as an
entity (person, place, or thing) or conversational text, in a compact
form.
Input chips can be made selectable by setting
onSelected,
deletable by setting
onDeleted,
and pressable like a button with
onPressed.
They have a
label,
and they can have a leading icon (see
avatar)
and a trailing icon
(deleteIcon).
Colors and padding can be customized.
Here is my simple interpretation of InputChip:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: _toContainer(),
),
),
Divider(
color: Colors.blueGrey,
height: 10.0,
),
Align(
alignment: Alignment.centerLeft,
child: _subjectContainer(),
),
Divider(
color: Colors.blueGrey,
height: 10.0,
),
Align(
alignment: Alignment.centerLeft,
child: _messageContainer(),
),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _messageContainer() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Text(
'Message',
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
),
);
}
Widget _toContainer() {
return Wrap(
spacing: 5.0,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8.0, right: 8.0),
child: Container(
child: Text(
'To',
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
),
),
Container(
child: _profileChips("Scott Hill",
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80"),
),
],
);
}
Widget _subjectContainer() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Text(
'Subject',
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
),
);
}
Widget _profileChips(String myName, String myImage) {
return Material(
child: InputChip(
avatar: CircleAvatar(
backgroundColor: Colors.blueGrey,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(myImage),
)),
),
),
label: Text(myName),
labelStyle: TextStyle(
color: Colors.black, fontSize: 14.0, fontWeight: FontWeight.bold),
onPressed: () {},
onDeleted: () {},
),
);
}
}
Output:
And for a fully functional example, I've tested the answer in here.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// See: https://twitter.com/shakil807/status/1042127387515858949
// https://github.com/pchmn/MaterialChipsInput/tree/master/library/src/main/java/com/pchmn/materialchips
// https://github.com/BelooS/ChipsLayoutManager
void main() => runApp(ChipsDemoApp());
class ChipsDemoApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.indigo,
accentColor: Colors.pink,
),
home: DemoScreen(),
);
}
}
class DemoScreen extends StatefulWidget {
#override
_DemoScreenState createState() => _DemoScreenState();
}
class _DemoScreenState extends State<DemoScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Material Chips Input'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: const InputDecoration(hintText: 'normal'),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ChipsInput<AppProfile>(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search), hintText: 'Profile search'),
findSuggestions: _findSuggestions,
onChanged: _onChanged,
chipBuilder: (BuildContext context,
ChipsInputState<AppProfile> state, AppProfile profile) {
return InputChip(
key: ObjectKey(profile),
label: Text(profile.name),
avatar: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
onDeleted: () => state.deleteChip(profile),
onSelected: (_) => _onChipTapped(profile),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
);
},
suggestionBuilder: (BuildContext context,
ChipsInputState<AppProfile> state, AppProfile profile) {
return ListTile(
key: ObjectKey(profile),
leading: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
title: Text(profile.name),
subtitle: Text(profile.email),
onTap: () => state.selectSuggestion(profile),
);
},
),
),
),
],
),
);
}
void _onChipTapped(AppProfile profile) {
print('$profile');
}
void _onChanged(List<AppProfile> data) {
print('onChanged $data');
}
Future<List<AppProfile>> _findSuggestions(String query) async {
if (query.length != 0) {
return mockResults.where((profile) {
return profile.name.contains(query) || profile.email.contains(query);
}).toList(growable: false);
} else {
return const <AppProfile>[];
}
}
}
// -------------------------------------------------
const mockResults = <AppProfile>[
AppProfile('Stock Man', 'stock#man.com',
'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
AppProfile('Paul', 'paul#google.com',
'https://mbtskoudsalg.com/images/person-stock-image-png.png'),
AppProfile('Fred', 'fred#google.com',
'https://media.istockphoto.com/photos/feeling-great-about-my-corporate-choices-picture-id507296326'),
AppProfile('Bera', 'bera#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('John', 'john#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Thomas', 'thomas#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Norbert', 'norbert#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Marina', 'marina#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
];
class AppProfile {
final String name;
final String email;
final String imageUrl;
const AppProfile(this.name, this.email, this.imageUrl);
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppProfile &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
#override
String toString() {
return 'Profile{$name}';
}
}
// -------------------------------------------------
typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query);
typedef ChipSelected<T> = void Function(T data, bool selected);
typedef ChipsBuilder<T> = Widget Function(
BuildContext context, ChipsInputState<T> state, T data);
class ChipsInput<T> extends StatefulWidget {
const ChipsInput({
Key key,
this.decoration = const InputDecoration(),
#required this.chipBuilder,
#required this.suggestionBuilder,
#required this.findSuggestions,
#required this.onChanged,
this.onChipTapped,
}) : super(key: key);
final InputDecoration decoration;
final ChipsInputSuggestions findSuggestions;
final ValueChanged<List<T>> onChanged;
final ValueChanged<T> onChipTapped;
final ChipsBuilder<T> chipBuilder;
final ChipsBuilder<T> suggestionBuilder;
#override
ChipsInputState<T> createState() => ChipsInputState<T>();
}
class ChipsInputState<T> extends State<ChipsInput<T>>
implements TextInputClient {
static const kObjectReplacementChar = 0xFFFC;
Set<T> _chips = Set<T>();
List<T> _suggestions;
int _searchId = 0;
FocusNode _focusNode;
TextEditingValue _value = TextEditingValue();
TextInputConnection _connection;
String get text => String.fromCharCodes(
_value.text.codeUnits.where((ch) => ch != kObjectReplacementChar),
);
bool get _hasInputConnection => _connection != null && _connection.attached;
void requestKeyboard() {
if (_focusNode.hasFocus) {
_openInputConnection();
} else {
FocusScope.of(context).requestFocus(_focusNode);
}
}
void selectSuggestion(T data) {
setState(() {
_chips.add(data);
_updateTextInputState();
_suggestions = null;
});
widget.onChanged(_chips.toList(growable: false));
}
void deleteChip(T data) {
setState(() {
_chips.remove(data);
_updateTextInputState();
});
widget.onChanged(_chips.toList(growable: false));
}
#override
void initState() {
super.initState();
_focusNode = FocusNode();
_focusNode.addListener(_onFocusChanged);
}
void _onFocusChanged() {
if (_focusNode.hasFocus) {
_openInputConnection();
} else {
_closeInputConnectionIfNeeded();
}
setState(() {
// rebuild so that _TextCursor is hidden.
});
}
#override
void dispose() {
_focusNode?.dispose();
_closeInputConnectionIfNeeded();
super.dispose();
}
void _openInputConnection() {
if (!_hasInputConnection) {
_connection = TextInput.attach(this, TextInputConfiguration());
_connection.setEditingState(_value);
}
_connection.show();
}
void _closeInputConnectionIfNeeded() {
if (_hasInputConnection) {
_connection.close();
_connection = null;
}
}
#override
Widget build(BuildContext context) {
var chipsChildren = _chips
.map<Widget>(
(data) => widget.chipBuilder(context, this, data),
)
.toList();
final theme = Theme.of(context);
chipsChildren.add(
Container(
height: 32.0,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
text,
style: theme.textTheme.subtitle1.copyWith(
height: 1.5,
),
),
_TextCaret(
resumed: _focusNode.hasFocus,
),
],
),
),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
//mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: requestKeyboard,
child: InputDecorator(
decoration: widget.decoration,
isFocused: _focusNode.hasFocus,
isEmpty: _value.text.length == 0,
child: Wrap(
children: chipsChildren,
spacing: 4.0,
runSpacing: 4.0,
),
),
),
Expanded(
child: ListView.builder(
itemCount: _suggestions?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
return widget.suggestionBuilder(
context, this, _suggestions[index]);
},
),
),
],
);
}
#override
void updateEditingValue(TextEditingValue value) {
final oldCount = _countReplacements(_value);
final newCount = _countReplacements(value);
setState(() {
if (newCount < oldCount) {
_chips = Set.from(_chips.take(newCount));
}
_value = value;
});
_onSearchChanged(text);
}
int _countReplacements(TextEditingValue value) {
return value.text.codeUnits
.where((ch) => ch == kObjectReplacementChar)
.length;
}
#override
void performAction(TextInputAction action) {
_focusNode.unfocus();
}
void _updateTextInputState() {
final text =
String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));
_value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
composing: TextRange(start: 0, end: text.length),
);
_connection.setEditingState(_value);
}
void _onSearchChanged(String value) async {
final localId = ++_searchId;
final results = await widget.findSuggestions(value);
if (_searchId == localId && mounted) {
setState(() => _suggestions = results
.where((profile) => !_chips.contains(profile))
.toList(growable: false));
}
}
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _TextCaret extends StatefulWidget {
const _TextCaret({
Key key,
this.duration = const Duration(milliseconds: 500),
this.resumed = false,
}) : super(key: key);
final Duration duration;
final bool resumed;
#override
_TextCursorState createState() => _TextCursorState();
}
class _TextCursorState extends State<_TextCaret>
with SingleTickerProviderStateMixin {
bool _displayed = false;
Timer _timer;
#override
void initState() {
super.initState();
_timer = Timer.periodic(widget.duration, _onTimer);
}
void _onTimer(Timer timer) {
setState(() => _displayed = !_displayed);
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return FractionallySizedBox(
heightFactor: 0.7,
child: Opacity(
opacity: _displayed && widget.resumed ? 1.0 : 0.0,
child: Container(
width: 2.0,
color: theme.primaryColor,
),
),
);
}
}
Working output:
Aside from the samples above, you have the option to use flutter_chips_input plugin.
Flutter library for building input fields with InputChips as input
options.
Here is an example: