When the user agrees to the photo, I want to send the photo back to the homepage where they can access the photo later on.
Currently, I am just opening the camera again on the PhotoPreview page when the user clicks the second button (OutlineButton). Instead, I want this photo to be sent to the homepage.
Here is the relevant portion of the PhotoPreview page
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pop(
context), // Go back to the camera to take the picture again
child: Icon(Icons.camera_alt),
),
appBar: AppBar(title: Text('Photo Preview')),
body: Column(children: [
Expanded(child: Image.file(File(widget.imagePath))),
const SizedBox(height: 16.0),
OutlineButton(
onPressed: () {
_openGallery();
Navigator.pop(context);
},
child: Text('Okay'),
borderSide: BorderSide(color: Color(0xff33333D)),
),
]),
);
}
}
The Gridview on my home page, which renders the photo in the format I want, is as such
: GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
childAspectRatio: (80 / 150),
padding: const EdgeInsets.all(2.0),
children:
List.generate(widget.imageArray.length, (index) {
return Container(
decoration: new BoxDecoration(
color: const Color(0xff000000),
borderRadius: BorderRadius.circular(10),
image: new DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.4),
BlendMode.dstATop),
How can I connect the two, for when the user clicks the OutlineButton that it sends the photo on the preview page to the home screen in the format above?
Edit per answer: Here is full Homepage
import 'dart:io';
import 'package:flutter/material.dart';
class Homepage_1 extends StatefulWidget {
final List<File> imageArray;
Homepage_1({Key key, this.imageArray}) : super(key: key);
#override
_Homepage_1State createState() => _Homepage_1State();
}
class _Homepage_1State extends State<Homepage_1> {
var image;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.white,
body: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Padding(
padding:
const EdgeInsets.only(top: 100, left: 40, right: 0, bottom: 0),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(
'App Name',
style: TextStyle(
fontSize: 60,
fontFamily: 'Avenir',
fontWeight: FontWeight.w900,
),
),
Container(
margin:
EdgeInsets.only(top: 0, left: 0, right: 50, bottom: 0),
child: widget.imageArray.isEmpty
? Column(children: [
Text(
'Yikes! You have no photos',
style: TextStyle(
fontSize: 19,
fontFamily: 'Avenir',
fontWeight: FontWeight.w900,
),
),
Text(
'Click the circular button below'
style: TextStyle(
fontSize: 15,
fontFamily: 'Avenir',
fontWeight: FontWeight.w500,
),
),
])
: GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
childAspectRatio: (80 / 150),
padding: const EdgeInsets.all(2.0),
children:
List.generate(widget.imageArray.length, (index) {
return Container(
decoration: new BoxDecoration(
color: const Color(0xff000000),
borderRadius: BorderRadius.circular(10),
image: new DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.4),
BlendMode.dstATop),
),
),
);
})))
]),
)
]));
}
}
& here is full Photo preview screen:
import 'package:flutter/material.dart';
import 'dart:io';
class PhotoPreviewScreen extends StatefulWidget {
Function setData;
final String imagePath;
PhotoPreviewScreen({Key key, this.setData, this.imagePath}) : super(key: key);
_PhotoPreviewScreenState createState() => _PhotoPreviewScreenState();
}
class _PhotoPreviewScreenState extends State<PhotoPreviewScreen> {
var image;
Future _openGallery() async {
if (widget.setData != null) {
widget.setData(File(image.path));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pop(
context), // Go back to the camera to take the picture again
child: Icon(Icons.camera_alt),
),
appBar: AppBar(title: Text('Photo Preview')),
body: Column(children: [
Expanded(child: Image.file(File(widget.imagePath))),
const SizedBox(height: 16.0),
OutlineButton(
onPressed: () async {
await _openGallery();
Navigator.of(context).pop(widget.imagePath);
},
child: Text('Okay'),
borderSide: BorderSide(color: Color(0xff33333D)),
),
]),
);
}
}
You can pass arguments to pop method and received that as a return value of push .
I wrote a minimal sample for you. Hopefully you get the idea, but if you have any questions, please don't hesitate to ask!
class PhotoPreviewPage extends StatefulWidget {
const PhotoPreviewPage({Key? key, #required this.imagePath})
: super(key: key);
#override
_PhotoPreviewPageState createState() => _PhotoPreviewPageState();
final String imagePath;
}
class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: OutlineButton(
onPressed: () {
Navigator.of(context).pop(widget.imagePath);
},
child: const Text('OK'),
),
),
);
}
}
/// This is a overly simplified version of the CameraPage
/// Basically, you take a photo and pass that to cameraPreview page
class CameraPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: () async {
final imagePath = await _takeAPhoto();
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => PhotoPreviewPage(imagePath: imagePath),
),
);
},
child: Text('take a photo'),
),
),
);
}
Future<String> _takeAPhoto() {
// some logic to take a photo and return imagePath
return imagePath;
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
final List<File> imageArray = [];
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
crossAxisCount: 2,
children: List.generate(
widget.imageArray.length,
(index) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
),
),
),
),
),
bottomNavigationBar: Row(
children: [
IconButton(
onPressed: () async {
final filePath = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PhotoPreviewPage(),
),
);
if (filePath != null) {
setState(() {
widget.imageArray.add(File(filePath));
});
}
},
icon: Icon(Icons.camera),
),
],
),
);
}
}
But depending on the exact page structure of your app, you might need to look into state management solutions like Bloc or Riverpod.
Related
I am using ShowModalBottomSheet . I want to open it from left side not from bottom in flutter..
To achieve you desire UI requirements , you should create a custom modal sheet.
It is very simple , you just have to make a container and use tween animation to make it visible and it works like a modal sheet,You can change the direction of the container from its start animating and many more.
I will try to add a code for it later.
This package help you to achieve side modal sheet.
https://pub.dev/packages/side_sheet
Or there is one more way, you should visit this link,I hope it will fullfill your requirements.
https://pub.dev/packages/modal_side_sheet
I think the first package can be more helpfull for you because it has some customization options like you can choose the side etc.
in case someone still needs help. You can use a combination of overlay and Tween to create a custom side sheet.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Custom Sidesheet',
theme: ThemeData(useMaterial3: true),
home: const MyHomePage(title: 'Custom Sidesheet'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
OverlayEntry? sideSheetOverlayEntry;
final sideSheetOverlayLayerLink = LayerLink();
bool isSidebarShown = false;
#override
void initState() {
super.initState();
}
void showSideSheet() {
final sideSheetOverlay = Overlay.of(context)!;
sideSheetOverlayEntry = OverlayEntry(
builder: (context) => Positioned(
height: MediaQuery.of(context).size.height,
child: isSidebarShown
? CompositedTransformFollower(
link: sideSheetOverlayLayerLink,
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 150, end: 300),
duration: const Duration(milliseconds: 300),
builder: (BuildContext context, double size, Widget? child) {
return Material(
child: SafeArea(
child: Container(
width: size,
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () {
setState(() {
isSidebarShown = false;
showSideSheet();
});
},
onHover: (value) {},
child: const Icon(
Icons.close,
color: Colors.redAccent,
size: 25.0,
),
)
],
),
Column(
children: [
Container(
child: Text(
"Custom Side Sheet",
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.blueGrey),
),
),
const Divider(
height: 5.0,
color: Colors.black,
),
Container(
child: Row(children: [
Icon(
Icons.dashboard,
size: 25.0,
color: Colors.blueGrey,
),
Text(
"Home",
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.blueGrey, fontSize: 25.0, fontWeight: FontWeight.w300),
)
]),
)
],
)
],
)),
),
);
},
))
: SizedBox.shrink(),
),
);
sideSheetOverlay.insert(sideSheetOverlayEntry!);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
leading: GestureDetector(
onTap: () {
setState(() {
isSidebarShown = true;
showSideSheet();
});
},
child: Icon(
Icons.menu_sharp,
size: 30.0,
),
),
),
body: Container(
color: Color.fromRGBO(223, 230, 233, 1.0),
)),
);
}
}
side sheet not active image
side sheet active image
I want to show the snack bar or Dialog on any screen in the Flutter app, anyone knows the way for that ?
For example.. let's say, I receive a notification when the user is in-app, on any screen in-app. How can I display the snack bar message in that situation regardless of which screen the user is currently on?
You can do something like that :
pip_flutter: ^0.0.3
Example:
import 'package:flutter/material.dart';
import 'package:pip_flutter/pipflutter_player.dart';
import 'package:pip_flutter/pipflutter_player_configuration.dart';
import 'package:pip_flutter/pipflutter_player_controller.dart';
import 'package:pip_flutter/pipflutter_player_data_source.dart';
import 'package:pip_flutter/pipflutter_player_data_source_type.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.pink,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Picture in Picture Mode'),
),
body: Center(
child: InkWell(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PictureInPicturePage()));
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Center(
child: Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
decoration: BoxDecoration(color: Colors.pink,borderRadius: BorderRadius.circular(12.0)),
child: const Text(
'Picture in Picture Mode',
style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 16),
),
),
),
),
),
),
);
}
}
class PictureInPicturePage extends StatefulWidget {
#override
_PictureInPicturePageState createState() => _PictureInPicturePageState();
}
class _PictureInPicturePageState extends State<PictureInPicturePage> {
late PipFlutterPlayerController pipFlutterPlayerController;
final GlobalKey pipFlutterPlayerKey = GlobalKey();
#override
void initState() {
PipFlutterPlayerConfiguration pipFlutterPlayerConfiguration =
const PipFlutterPlayerConfiguration(
aspectRatio: 16 / 9,
fit: BoxFit.contain,
);
PipFlutterPlayerDataSource dataSource = PipFlutterPlayerDataSource(
PipFlutterPlayerDataSourceType.network,
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
);
pipFlutterPlayerController =
PipFlutterPlayerController(pipFlutterPlayerConfiguration);
pipFlutterPlayerController.setupDataSource(dataSource);
pipFlutterPlayerController
.setPipFlutterPlayerGlobalKey(pipFlutterPlayerKey);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Picture in Picture player"),
leading: IconButton(onPressed: (){
Navigator.of(context).pop();
}, icon: const Icon(Icons.arrow_back_ios,color: Colors.white,)),
),
body: Column(
children: [
const SizedBox(height: 20),
Flexible(
flex: 1,
fit: FlexFit.loose,
child: AspectRatio(
aspectRatio: 16 / 9,
child: PipFlutterPlayer(
controller: pipFlutterPlayerController,
key: pipFlutterPlayerKey,
),
),
),
Container(
margin: const EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
child: Container(
width: MediaQuery.of(context).size.width * 0.4,
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
decoration: BoxDecoration(color: Colors.pink,borderRadius: BorderRadius.circular(12.0)),
child: const Center(child: Text("Show PiP",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),))),
onTap: () {
pipFlutterPlayerController
.enablePictureInPicture(pipFlutterPlayerKey);
},
),
InkWell(
child: Container(
width: MediaQuery.of(context).size.width * 0.4,
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
decoration: BoxDecoration(color: Colors.pink,borderRadius: BorderRadius.circular(12.0)),
child: Center(child: const Text("Disable PiP",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),))),
onTap: () async {
pipFlutterPlayerController.disablePictureInPicture();
},
),
],
),
),
],
),
);
}
}
I have product items in grid view which is a future builder, and wrapped with Hero Widget and gave a unique tag by id, and in detail new page also I wrapped with Hero Widget and gave same unique tag but the animation is working only when coming back to screen. I didn't understand why Hero animation is not working when navigating to a new page, maybe because of Future builder? or I made any mistake? don't know what happening, Can anyone Help me to achieve nice Hero animation. Below I provided my code. Please feel free to ask any questions. Thanks in advance.
main.dart
import 'package:flutter/material.dart';
import 'package:httprequest/screens/all_products.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const AllProductsScreen(),
);
}
}
all_products.dart
import 'package:flutter/material.dart';
import 'package:httprequest/screens/single_product.dart';
import 'package:httprequest/services/api_services.dart';
class AllProductsScreen extends StatefulWidget {
const AllProductsScreen({Key? key}) : super(key: key);
#override
_AllProductsScreenState createState() => _AllProductsScreenState();
}
class _AllProductsScreenState extends State<AllProductsScreen> {
Future ? products;
#override
void initState() {
// TODO: implement initState
products = ApiServices().getAllProducts();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Products"),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: FutureBuilder(
future: products,
builder: (context, AsyncSnapshot snapshot){
if(snapshot.hasData){
return Center(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 2 / 3,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext ctx, index) {
return GestureDetector(
child: Hero(
tag: snapshot.data[index]["id"],
child: Card(
child: Container(
padding: EdgeInsets.all(5.0),
child: Column(
children: [
Image.network(snapshot.data[index]["image"],height: 180,width: 180,),
Text(snapshot.data[index]["title"],textAlign: TextAlign.center,maxLines: 2,overflow: TextOverflow.ellipsis,),
Text("\$: ${snapshot.data[index]["price"]}")
],
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15)),
),
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => SingleProduct(snapshot.data[index]["id"])));
},
);
}),
);
}
return const Center(child: CircularProgressIndicator(),);
},
),
),
);
}
}
single_product.dart
import 'package:flutter/material.dart';
import 'package:httprequest/services/api_services.dart';
class SingleProduct extends StatefulWidget {
final id;
SingleProduct(this.id);
#override
_SingleProductState createState() => _SingleProductState();
}
class _SingleProductState extends State<SingleProduct> {
Future ? product;
#override
void initState() {
// TODO: implement initState
product = ApiServices().getSingleProduct(widget.id);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Products"),
),
body: FutureBuilder(
future: product,
builder: (context,AsyncSnapshot snapshot){
if(snapshot.hasData){
return Container(
color: Colors.white,
child: Column(
children: [
Hero(
tag: widget.id,
child: Container(
color: Colors.transparent,
child: Center(
child: Image.network(snapshot.data["image"],height: 200,width: 200,),
),
),
),
Expanded(
child: Container(
color: Colors.transparent,
child: Card(
color: Colors.white,
elevation: 20.0,
shape: RoundedRectangleBorder(
//side: BorderSide(width: 0.2),
borderRadius: BorderRadius.only(topRight: Radius.circular(20),topLeft: Radius.circular(20))),
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(snapshot.data["title"],textAlign: TextAlign.center,style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16),),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("\$: ${snapshot.data["price"]}",style: TextStyle(fontSize: 16),),
Row(
children: [
Text(snapshot.data["rating"]["rate"].toString(),style: TextStyle(fontSize: 16),),
Icon(Icons.star,color: Colors.yellow,size: 20,),
],
),
],
),
SizedBox(height: 5,),
Text(("Category: ${snapshot.data["category"]}"),textAlign: TextAlign.left,style: TextStyle(fontSize: 16),),
SizedBox(height: 5,),
Text(snapshot.data["description"],textAlign: TextAlign.justify,style: TextStyle(fontSize: 16),),
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(50),
color: Colors.black,
),
height: 50,
width: 130,
//color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.shopping_cart,color: Colors.white,),
Text("Add to cart",style: TextStyle(color: Colors.white),),
],
),
),
),
],
),
),
),
),
),
],
),
);
}
return Center(child: CircularProgressIndicator());
},
),
);
}
}
api_services.dart
import 'dart:developer';
import 'dart:convert';
import 'package:http/http.dart' as http;
class ApiServices {
Future getAllProducts() async {
var allProcuctsUri = Uri.parse('https://fakestoreapi.com/products');
var response = await http.get(allProcuctsUri);
log("All Products response : ${response.statusCode.toString()}");
log("All Products body : ${response.body}");
return json.decode(response.body);
}
Future getSingleProduct(int id) async {
var singleProcuctUri = Uri.parse('https://fakestoreapi.com/products/${id}');
var response = await http.get(singleProcuctUri);
log("Single Product response : ${response.statusCode.toString()}");
log("Single Product body : ${response.body}");
return json.decode(response.body);
}
}
you have to provide the same tag in hero widget to both the screens which you want to animate. you have wrap the widget which to animate with HeroAnimation and provide tag and then wrap the other screen with HeroAnimation and provide the same tag to both the HeroAnimation widgets..
to check whether two tags are same first print snapshot.data[index]["id"] of all_products.dart and widget.id of single_product.dart.
if the second page tag is getting null, please initialize a variable inside build of single_product.dart like
#override
String finalid=widget.id; //add this and get reference from this
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Products"),
),
it would be better if both tags are in String format.
I am struggling with this Chat Screen. The app is meant to ask questions (not part of the below code) and the user either selects answers or types them. When the user types a first answer everything goes according to the plan and a first message is displayed. However the app then goes on displaying the second answer twice, the third one three times and so on.
I have been facing this issue for a few days and I cannot figure out why the app behaves the way it does. Could you please take a look at the code and suggest a way to fix this?
To give you some background information, this Chat Screen is part of a larger application. It should subscribe to a stream when the user opens the app. Then each message is pushed to the stream, whether it is a question asked by the bot or an answer given by the User. The system listens to the stream and displays a new message each time the stream broadcasts something, in our case the latest user input.
I am using a list of message models built from the stream to display the messages. For the purpose of asking this question I simplified the model to the extreme but in practice it has 23 fields. Creating this list of messages is the best solution I managed to think of but there may be a better way to handle this situation. Feel free to let me know if you know of any.
Here is the code that I am running.
import 'package:flutter/material.dart';
import 'dart:async';
StreamController<ChatMessageModel> _chatMessagesStreamController = StreamController<ChatMessageModel>.broadcast();
Stream _chatMessagesStream = _chatMessagesStreamController.stream;
const Color primaryColor = Color(0xff6DA7B9);
const Color secondaryColor = Color(0xffF0F0F0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat Screen',
home: ChatScreen(),
);
}
}
class ChatMessageModel {
final String message;
const ChatMessageModel({
this.message,
}
);
factory ChatMessageModel.turnSnapshotIntoListRecord(Map data) {
return ChatMessageModel(
message: data['message'],
);
}
#override
List<Object> get props => [
message,
];
}
class ChatScreen extends StatefulWidget {
static const String id = 'chat_screen9';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _messageTextController = TextEditingController();
String _userInput;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: secondaryColor,
appBar: AppBar(
title: Row(
children: [
Container(
padding: EdgeInsets.all(8.0),
child: Text('Chat Screen',
style: TextStyle(color: Colors.white,),
),
)
],
),
backgroundColor: primaryColor,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MessagesStream(),
Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: primaryColor,
width: 1.0,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _messageTextController,
onChanged: (value) {
_userInput = value;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
hintText: 'Type your answer here',
// border: InputBorder.none,
),
),
),
TextButton(
onPressed: () {
_messageTextController.clear();
debugPrint('Adding a ChatMessageModel with the message $_userInput to the Stream');
ChatMessageModel chatMessageModelRecord = ChatMessageModel(message: _userInput);
_chatMessagesStreamController.add(chatMessageModelRecord,);
},
child: Text(
'OK',
style: TextStyle(
color: primaryColor,
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatelessWidget {
List<ChatMessageModel> _allMessagesContainedInTheStream = [];
#override
Widget build(BuildContext context) {
return StreamBuilder<ChatMessageModel>(
stream: _chatMessagesStream,
builder: (context, snapshot) {
_chatMessagesStream.listen((streamedMessages) {
// _allMessagesContainedInTheStream.clear();
debugPrint('Value from controller: $streamedMessages');
_allMessagesContainedInTheStream.add(streamedMessages);
}
);
return Expanded(
child: ListView.builder(
// reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
itemCount: _allMessagesContainedInTheStream.length,
itemBuilder: (BuildContext context, int index) {
if (snapshot.hasData) {
return UserChatBubble(chatMessageModelRecord: _allMessagesContainedInTheStream[index]);
}
},
),
);
},
);
}
}
class UserChatBubble extends StatelessWidget {
final ChatMessageModel chatMessageModelRecord;
const UserChatBubble({
Key key,
#required this.chatMessageModelRecord,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 5,),
child: Container(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 7 / 10,),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(15.0),
bottomRight: Radius.circular(15.0),
topLeft: Radius.circular(15.0),
),
color: primaryColor,
),
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 20,),
child: Text(chatMessageModelRecord.message,
style: TextStyle(
fontSize: 17,
// fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
);
}
}
First of all, thank you for the interesting problem and functioning example provided. I had to do some small changes to convert it to "null-safety", but my code should work on your computer too.
The only problem you had initialization of _chatMessagesStream listener. You should do it only once and ideally in initState, to call it only once.
So here is the fix for you:
class MessagesStream extends StatefulWidget {
#override
_MessagesStreamState createState() => _MessagesStreamState();
}
class _MessagesStreamState extends State<MessagesStream> {
final List<ChatMessageModel> _allMessagesContainedInTheStream = [];
#override
void initState() {
_chatMessagesStream.listen((streamedMessages) {
// _allMessagesContainedInTheStream.clear();
debugPrint('Value from controller: $streamedMessages');
_allMessagesContainedInTheStream.add(streamedMessages);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<ChatMessageModel>(
stream: _chatMessagesStream,
builder: (context, snapshot) {
return Expanded(
child: ListView.builder(
// reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
itemCount: _allMessagesContainedInTheStream.length,
itemBuilder: (BuildContext context, int index) {
if (snapshot.hasData) {
return UserChatBubble(
chatMessageModelRecord:
_allMessagesContainedInTheStream[index],
);
} else {
print(snapshot.connectionState);
return Container();
}
},
),
);
},
);
}
}
Also providing full code for null-safety just in case!
import 'package:flutter/material.dart';
import 'dart:async';
final StreamController<ChatMessageModel> _chatMessagesStreamController =
StreamController<ChatMessageModel>.broadcast();
final Stream<ChatMessageModel> _chatMessagesStream =
_chatMessagesStreamController.stream;
const Color primaryColor = Color(0xff6DA7B9);
const Color secondaryColor = Color(0xffF0F0F0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat Screen',
home: ChatScreen(),
);
}
}
class ChatMessageModel {
final String? message;
const ChatMessageModel({
this.message,
});
factory ChatMessageModel.turnSnapshotIntoListRecord(Map data) {
return ChatMessageModel(
message: data['message'],
);
}
List<Object> get props => [
message!,
];
}
class ChatScreen extends StatefulWidget {
static const String id = 'chat_screen9';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _messageTextController = TextEditingController();
String? _userInput;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: secondaryColor,
appBar: AppBar(
title: Row(
children: [
Container(
padding: EdgeInsets.all(8.0),
child: Text(
'Chat Screen',
style: TextStyle(
color: Colors.white,
),
),
)
],
),
backgroundColor: primaryColor,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MessagesStream(),
Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: primaryColor,
width: 1.0,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _messageTextController,
onChanged: (value) {
_userInput = value;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
hintText: 'Type your answer here',
// border: InputBorder.none,
),
),
),
TextButton(
onPressed: () {
_messageTextController.clear();
debugPrint(
'Adding a ChatMessageModel with the message $_userInput to the Stream');
ChatMessageModel chatMessageModelRecord =
ChatMessageModel(message: _userInput);
_chatMessagesStreamController.add(
chatMessageModelRecord,
);
},
child: Text(
'OK',
style: TextStyle(
color: primaryColor,
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatefulWidget {
#override
_MessagesStreamState createState() => _MessagesStreamState();
}
class _MessagesStreamState extends State<MessagesStream> {
final List<ChatMessageModel> _allMessagesContainedInTheStream = [];
#override
void initState() {
_chatMessagesStream.listen((streamedMessages) {
// _allMessagesContainedInTheStream.clear();
debugPrint('Value from controller: $streamedMessages');
_allMessagesContainedInTheStream.add(streamedMessages);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<ChatMessageModel>(
stream: _chatMessagesStream,
builder: (context, snapshot) {
return Expanded(
child: ListView.builder(
// reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
itemCount: _allMessagesContainedInTheStream.length,
itemBuilder: (BuildContext context, int index) {
if (snapshot.hasData) {
return UserChatBubble(
chatMessageModelRecord:
_allMessagesContainedInTheStream[index],
);
} else {
print(snapshot.connectionState);
return Container();
}
},
),
);
},
);
}
}
class UserChatBubble extends StatelessWidget {
final ChatMessageModel chatMessageModelRecord;
const UserChatBubble({
Key? key,
required this.chatMessageModelRecord,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 5,
),
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 7 / 10,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(15.0),
bottomRight: Radius.circular(15.0),
topLeft: Radius.circular(15.0),
),
color: primaryColor,
),
padding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 20,
),
child: Text(
"${chatMessageModelRecord.message}",
style: TextStyle(
fontSize: 17,
// fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
);
}
}
I want to load all the images from assets folder in my flutter app Select Picture screen. And when the user selects and image it will take half space in another screen. So it's very similar to the regular edit image functionality in our phone.
This is what I want after the user has selected an image.
I've successfully added all the images to a screen called gallery:
And this is how I did it:
import 'package:flutter/material.dart';
import 'package:flutter_app/src/components/ImageDetails.dart';
List<ImageDetails> _images = [
ImageDetails(
imagePath: 'assets/images/hut.png',
title: 'Hutt',
),
ImageDetails(
imagePath: 'assets/images/scenary.png',
title: 'Scenary',
),
ImageDetails(
imagePath: 'assets/images/menu.png',
title: 'Menu Bar',
),
];
class ImageSelection extends StatefulWidget {
#override
_ImageSelectionState createState() => _ImageSelectionState();
}
class _ImageSelectionState extends State<ImageSelection> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlueAccent,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(
height: 40,
),
Text(
'Gallery',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w600,
color: Colors.white,
),
textAlign: TextAlign.center,
),
SizedBox(
height: 40,
),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 30,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (context, index) {
return RawMaterialButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsPage(
imagePath: _images[index].imagePath,
title: _images[index].title,
index: index,
),
),
);
},
child: Hero(
tag: 'logo$index',
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
image: DecorationImage(
image: AssetImage(_images[index].imagePath),
fit: BoxFit.cover,
),
),
),
),
);
},
itemCount: _images.length,
),
),
)
],
),
),
);
}
}
class ImageDetails {
final String imagePath;
final String title;
ImageDetails({
#required this.imagePath,
#required this.title,
});
}
But I want to do this dynamically so if I add a new image in assets the application will automatically show the images. And on select the image will take below shown space in the canvas page?
import 'dart:collection';
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_image_gallery/flutter_image_gallery.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
Map<dynamic, dynamic> allImageInfo = new HashMap();
List allImage = new List();
#override
void initState() {
super.initState();
loadImageList();
}
Future<void> loadImageList() async {
Map<dynamic, dynamic> allImageTemp;
allImageTemp = await FlutterImageGallery.getAllImages;
print(" call $allImageTemp.length");
setState(() {
this.allImage = allImageTemp['URIList'] as List;
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
home: new Scaffold(
appBar: new AppBar(
title: const Text('Image Gallery'),
),
body: _buildGrid(),
),
);
}
Widget _buildGrid() {
return GridView.extent(
maxCrossAxisExtent: 150.0,
padding: const EdgeInsets.all(4.0),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: _buildGridTileList(allImage.length));
}
List<Container> _buildGridTileList(int count) {
return List<Container>.generate(
count,
(int index) => Container(
child: new Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Image.file(
File(allImage[index].toString()),
width: 96.0,
height: 96.0,
fit: BoxFit.contain,
),
],
)));
}
}
Dont Forget to Import :
dependencies:
flutter_image_gallery: ^1.0.6