Related
I've been trying to customize Flutter SearchDelgate to the type of search field I want it to be. It got a method named appBarTheme with return type ThemeData. Usually using ThemeData you can change the appbar theme but it's not making any change in my case. I am able to customize the hint text style searchFieldStyle method but nothing more.
here is code:
class CustomSearchDelegate extends SearchDelegate<Country> {
#override
ThemeData appBarTheme(BuildContext context) {
return ThemeData(
appBarTheme: AppBarTheme(
elevation: 0,
color: themeColor,
//app bar color I wanted
),
);
}
#override
TextStyle get searchFieldStyle => TextStyle(
color: whiteTextColor,
fontWeight: FontWeight.w600,
fontFamily: GoogleFonts.poppins().fontFamily,
);
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(
Icons.close_rounded,
color: Colors.white,
),
onPressed: () => query = '',
),
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
return Column(
children: [],
);
}
#override
Widget buildSuggestions(BuildContext context) {
return Column(
children: [],
);
}
}
It would be super helpful if someone could help me out with this.
also, a similar question has been raised before but never got answered
Flutter create custom search UI extends SearchDelegate
I found one way to customize flutter search delegate the way you want. you just have to copy flutter's search delegates code and then customize the code you want.
Here is the Solution:
1: this is the code of showSearch.
Container(
padding: EdgeInsets.only(left: 15.w, right: 15.w, top: 15.h, bottom: 15.h),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5.r)),
child: CustomSearchButton(
onTap: () async {
final String? result = await showSearchForCustomiseSearchDelegate(
context: context,
delegate: SearchScreen(
hintText: AppLocalizations.of(context)!.searchHere,
),
);
},
),
),
2: this is the code of customised flutter searchDelegate.
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = widget.delegate.appBarTheme(context);
final String searchFieldLabel = widget.delegate.searchFieldLabel ?? MaterialLocalizations.of(context).searchFieldLabel;
Widget? body;
switch (widget.delegate._currentBody) {
case _SearchBody.suggestions:
body = KeyedSubtree(
key: const ValueKey<_SearchBody>(_SearchBody.suggestions),
child: widget.delegate.buildSuggestions(context),
);
break;
case _SearchBody.results:
body = KeyedSubtree(
key: const ValueKey<_SearchBody>(_SearchBody.results),
child: widget.delegate.buildResults(context),
);
break;
case null:
break;
}
late final String routeName;
switch (theme.platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
routeName = '';
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
routeName = searchFieldLabel;
}
return Semantics(
explicitChildNodes: true,
scopesRoute: true,
namesRoute: true,
label: routeName,
child: Theme(
data: theme,
child: Scaffold(
appBar: AppBar(
elevation: 0,
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
leadingWidth: 0,
titleSpacing: 0,
//leading: widget.delegate.buildLeading(context),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(flex: 1, child: widget.delegate.buildLeading(context)!),
Expanded(
flex: 6,
child: Container(
margin: EdgeInsets.only(right: 15.w),
decoration: const BoxDecoration(
color: AppColors.white,
),
child: TextField(
controller: widget.delegate._queryTextController,
//focusNode: focusNode,
onSubmitted: (String _) {
widget.delegate.showResults(context);
},
textInputAction: widget.delegate.textInputAction,
keyboardType: widget.delegate.keyboardType,
decoration: InputDecoration(
fillColor: AppColors.white,
filled: true,
isDense: true,
hintText: searchFieldLabel,
hintStyle: TextStyle(fontSize: 14.sp),
contentPadding: EdgeInsets.symmetric(horizontal: 10.w),
prefixIcon: widget.delegate._queryTextController.text.isNotEmpty
? null
: Padding(
padding: EdgeInsets.only(right: 5.w),
child: Image.asset(
AppImages.searchBoxIcon1,
scale: 3.5.sp,
),
),
suffixIcon: widget.delegate._queryTextController.text.isEmpty
? Image.asset(
AppImages.searchBoxIcon2,
scale: 3.5.sp,
)
: InkWell(
onTap: () {
widget.delegate._queryTextController.clear();
},
child: Image.asset(
AppImages.closeCircle,
scale: 3.5.sp,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8.r)),
borderSide: const BorderSide(width: 1, color: AppColors.primaryColor),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8.r)),
borderSide: const BorderSide(width: 1, color: AppColors.white),
),
border: OutlineInputBorder(
borderSide: const BorderSide(color: AppColors.primaryColor),
borderRadius: BorderRadius.all(Radius.circular(8.r)),
)),
),
// TextField(
// controller: widget.delegate._queryTextController,
// focusNode: focusNode,
// style: theme.textTheme.headline6,
// textInputAction: widget.delegate.textInputAction,
// keyboardType: widget.delegate.keyboardType,
// onSubmitted: (String _) {
// widget.delegate.showResults(context);
// },
// decoration: InputDecoration(hintText: searchFieldLabel),
// ),
),
),
],
),
actions: widget.delegate.buildActions(context),
bottom: widget.delegate.buildBottom(context),
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: body,
),
),
),
);
3: Here is the full code.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:test_app/constants/app_images.dart';
import 'package:test_app/theme/colors.dart';
import 'package:test_app/widgets/custom_buttons.dart';
class SearchScreen extends SearchDelegate<String> {
SearchScreen({
String? hintText,
}) : super(
searchFieldLabel: hintText,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
);
#override
List<Widget>? buildActions(BuildContext context) {
return [
Container(),
];
}
#override
Widget? buildLeading(BuildContext context) {
return CustomBackButton(onTap: () {
close(context, '');
});
}
#override
Widget buildResults(BuildContext context) {
return Container();
}
#override
Widget buildSuggestions(BuildContext context) {
return Container();
}
}
Future<T?> showSearchForCustomiseSearchDelegate<T>({
required BuildContext context,
required SearchDelegate<T> delegate,
String? query = '',
bool useRootNavigator = false,
}) {
assert(delegate != null);
assert(context != null);
assert(useRootNavigator != null);
delegate.query = query ?? delegate.query;
delegate._currentBody = _SearchBody.suggestions;
return Navigator.of(context, rootNavigator: useRootNavigator).push(_SearchPageRoute<T>(
delegate: delegate,
));
}
abstract class SearchDelegate<T> {
SearchDelegate({
this.searchFieldLabel,
this.searchFieldStyle,
this.searchFieldDecorationTheme,
this.keyboardType,
this.textInputAction = TextInputAction.search,
}) : assert(searchFieldStyle == null || searchFieldDecorationTheme == null);
Widget buildSuggestions(BuildContext context);
Widget buildResults(BuildContext context);
Widget? buildLeading(BuildContext context);
List<Widget>? buildActions(BuildContext context);
PreferredSizeWidget? buildBottom(BuildContext context) => null;
ThemeData appBarTheme(BuildContext context) {
assert(context != null);
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
assert(theme != null);
return theme.copyWith(
appBarTheme: AppBarTheme(
brightness: colorScheme.brightness,
backgroundColor: colorScheme.brightness == Brightness.dark ? Colors.grey[900] : Colors.white,
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
textTheme: theme.textTheme,
),
inputDecorationTheme: searchFieldDecorationTheme ??
InputDecorationTheme(
hintStyle: searchFieldStyle ?? theme.inputDecorationTheme.hintStyle,
border: InputBorder.none,
),
);
}
String get query => _queryTextController.text;
set query(String value) {
assert(query != null);
_queryTextController.text = value;
queryTextController.selection = TextSelection.fromPosition(TextPosition(offset: queryTextController.text.length));
}
void showResults(BuildContext context) {
_focusNode?.unfocus();
currentBody = SearchBody.results;
}
void showSuggestions(BuildContext context) {
assert(_focusNode != null, '_focusNode must be set by route before showSuggestions is called.');
_focusNode!.requestFocus();
currentBody = SearchBody.suggestions;
}
void close(BuildContext context, T result) {
_currentBody = null;
_focusNode?.unfocus();
Navigator.of(context)
..popUntil((Route<dynamic> route) => route == _route)
..pop(result);
}
final String? searchFieldLabel;
final TextStyle? searchFieldStyle;
final InputDecorationTheme? searchFieldDecorationTheme;
final TextInputType? keyboardType;
final TextInputAction textInputAction;
Animation<double> get transitionAnimation => _proxyAnimation;
// The focus node to use for manipulating focus on the search page. This is
// managed, owned, and set by the _SearchPageRoute using this delegate.
FocusNode? _focusNode;
final TextEditingController _queryTextController = TextEditingController();
final ProxyAnimation _proxyAnimation = ProxyAnimation(kAlwaysDismissedAnimation);
final ValueNotifier<_SearchBody?> _currentBodyNotifier = ValueNotifier<_SearchBody?>(null);
SearchBody? get currentBody => _currentBodyNotifier.value;
set _currentBody(_SearchBody? value) {
_currentBodyNotifier.value = value;
}
SearchPageRoute<T>? route;
}
enum _SearchBody {
suggestions,
results,
}
class _SearchPageRoute<T> extends PageRoute<T> {
_SearchPageRoute({
required this.delegate,
}) : assert(delegate != null) {
assert(
delegate._route == null,
'The ${delegate.runtimeType} instance is currently used by another active '
'search. Please close that search by calling close() on the SearchDelegate '
'before opening another search with the same delegate instance.',
);
delegate._route = this;
}
final SearchDelegate<T> delegate;
#override
Color? get barrierColor => null;
#override
String? get barrierLabel => null;
#override
Duration get transitionDuration => const Duration(milliseconds: 300);
#override
bool get maintainState => false;
#override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: animation,
child: child,
);
}
#override
Animation<double> createAnimation() {
final Animation<double> animation = super.createAnimation();
delegate._proxyAnimation.parent = animation;
return animation;
}
#override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return _SearchPage<T>(
delegate: delegate,
animation: animation,
);
}
#override
void didComplete(T? result) {
super.didComplete(result);
assert(delegate._route == this);
delegate._route = null;
delegate._currentBody = null;
}
}
class _SearchPage<T> extends StatefulWidget {
const _SearchPage({
required this.delegate,
required this.animation,
});
final SearchDelegate<T> delegate;
final Animation<double> animation;
#override
State<StatefulWidget> createState() => _SearchPageState<T>();
}
class _SearchPageState<T> extends State<_SearchPage<T>> {
FocusNode focusNode = FocusNode();
#override
void initState() {
super.initState();
widget.delegate._queryTextController.addListener(_onQueryChanged);
widget.animation.addStatusListener(_onAnimationStatusChanged);
widget.delegate._currentBodyNotifier.addListener(_onSearchBodyChanged);
focusNode.addListener(_onFocusChanged);
widget.delegate._focusNode = focusNode;
}
#override
void dispose() {
super.dispose();
widget.delegate._queryTextController.removeListener(_onQueryChanged);
widget.animation.removeStatusListener(_onAnimationStatusChanged);
widget.delegate._currentBodyNotifier.removeListener(_onSearchBodyChanged);
widget.delegate._focusNode = null;
focusNode.dispose();
}
void _onAnimationStatusChanged(AnimationStatus status) {
if (status != AnimationStatus.completed) {
return;
}
widget.animation.removeStatusListener(_onAnimationStatusChanged);
if (widget.delegate._currentBody == _SearchBody.suggestions) {
focusNode.requestFocus();
}
}
#override
void didUpdateWidget(_SearchPage<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.delegate != oldWidget.delegate) {
oldWidget.delegate._queryTextController.removeListener(_onQueryChanged);
widget.delegate._queryTextController.addListener(_onQueryChanged);
oldWidget.delegate._currentBodyNotifier.removeListener(_onSearchBodyChanged);
widget.delegate._currentBodyNotifier.addListener(_onSearchBodyChanged);
oldWidget.delegate._focusNode = null;
widget.delegate._focusNode = focusNode;
}
}
void _onFocusChanged() {
if (focusNode.hasFocus && widget.delegate._currentBody != _SearchBody.suggestions) {
widget.delegate.showSuggestions(context);
}
}
void _onQueryChanged() {
setState(() {
// rebuild ourselves because query changed.
});
}
void _onSearchBodyChanged() {
setState(() {
// rebuild ourselves because search body changed.
});
}
#override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = widget.delegate.appBarTheme(context);
final String searchFieldLabel = widget.delegate.searchFieldLabel ?? MaterialLocalizations.of(context).searchFieldLabel;
Widget? body;
switch (widget.delegate._currentBody) {
case _SearchBody.suggestions:
body = KeyedSubtree(
key: const ValueKey<_SearchBody>(_SearchBody.suggestions),
child: widget.delegate.buildSuggestions(context),
);
break;
case _SearchBody.results:
body = KeyedSubtree(
key: const ValueKey<_SearchBody>(_SearchBody.results),
child: widget.delegate.buildResults(context),
);
break;
case null:
break;
}
late final String routeName;
switch (theme.platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
routeName = '';
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
routeName = searchFieldLabel;
}
return Semantics(
explicitChildNodes: true,
scopesRoute: true,
namesRoute: true,
label: routeName,
child: Theme(
data: theme,
child: Scaffold(
appBar: AppBar(
elevation: 0,
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
leadingWidth: 0,
titleSpacing: 0,
//leading: widget.delegate.buildLeading(context),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(flex: 1, child: widget.delegate.buildLeading(context)!),
Expanded(
flex: 6,
child: Container(
margin: EdgeInsets.only(right: 15.w),
decoration: const BoxDecoration(
color: AppColors.white,
),
child: TextField(
controller: widget.delegate._queryTextController,
//focusNode: focusNode,
onSubmitted: (String _) {
widget.delegate.showResults(context);
},
textInputAction: widget.delegate.textInputAction,
keyboardType: widget.delegate.keyboardType,
decoration: InputDecoration(
fillColor: AppColors.white,
filled: true,
isDense: true,
hintText: searchFieldLabel,
hintStyle: TextStyle(fontSize: 14.sp),
contentPadding: EdgeInsets.symmetric(horizontal: 10.w),
prefixIcon: widget.delegate._queryTextController.text.isNotEmpty
? null
: Padding(
padding: EdgeInsets.only(right: 5.w),
child: Image.asset(
AppImages.searchBoxIcon1,
scale: 3.5.sp,
),
),
suffixIcon: widget.delegate._queryTextController.text.isEmpty
? Image.asset(
AppImages.searchBoxIcon2,
scale: 3.5.sp,
)
: InkWell(
onTap: () {
widget.delegate._queryTextController.clear();
},
child: Image.asset(
AppImages.closeCircle,
scale: 3.5.sp,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8.r)),
borderSide: const BorderSide(width: 1, color: AppColors.primaryColor),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8.r)),
borderSide: const BorderSide(width: 1, color: AppColors.white),
),
border: OutlineInputBorder(
borderSide: const BorderSide(color: AppColors.primaryColor),
borderRadius: BorderRadius.all(Radius.circular(8.r)),
)),
),
// TextField(
// controller: widget.delegate._queryTextController,
// focusNode: focusNode,
// style: theme.textTheme.headline6,
// textInputAction: widget.delegate.textInputAction,
// keyboardType: widget.delegate.keyboardType,
// onSubmitted: (String _) {
// widget.delegate.showResults(context);
// },
// decoration: InputDecoration(hintText: searchFieldLabel),
// ),
),
),
],
),
actions: widget.delegate.buildActions(context),
bottom: widget.delegate.buildBottom(context),
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: body,
),
),
),
);
}
}
4: OUTPUT
Output video
Unfortunately, you do not have full control over the Theme of the AppBar in the SearchDelegate since some of the theme property values that you specify in the appBarTheme are not assigned to the app bar widget used in SearchDelegate. You can take a look at the source code. It only takes the values specified in the ThemeData specified in MaterialApp theme property. In my case, I needed to change the cursor color but changing the color in the MaterialApp would also modify the color in TextFields used elsewhere.
One solution is you can change the color before even opening the SearchDelegate i.e before showSearch and change it back again to the original color after navigating back from showSearch.
I hope you all are doing well today. I have another flutter issue that I have been stuck on for the past few days now. I'm attempting to upload this data to my firestore instance, but my post button never seems to be triggering. I have attempted to print a statement from the method that it evokes, but I can't seem to get that to work either. I'm attempting to create a social media app, and any and all help would be appreciated.
My main goal is to get the post button to execute in upload.dart.
I have also included home.dart since the two classes are connected in terms of performance.
upload.dart
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttermedia/models/user.dart';
import 'package:fluttermedia/pages/home.dart';
import 'package:fluttermedia/widgets/progress.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:image/image.dart' as Im;
import 'package:uuid/uuid.dart';
class Upload extends StatefulWidget {
final User currentUser;
Upload({this.currentUser});
#override
_UploadState createState() => _UploadState();
}
class _UploadState extends State<Upload> {
TextEditingController locationController = TextEditingController();
TextEditingController captionController = TextEditingController();
File file;
bool isUploading = false;
String postId = Uuid().v4();
handleChooseFromGallery() async{
Navigator.pop(context);
File file = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
this.file = file;
});
}
handleTakePhoto() async {
Navigator.pop(context);
File file = await ImagePicker.pickImage(source: ImageSource.camera,maxHeight: 675,maxWidth: 960);
setState(() {
this.file = file;
});
}
selectImage(parentContext){
return showDialog(
context: parentContext,
builder: (context) {
return SimpleDialog(
title: Text("Create Post"),
children: <Widget>[
SimpleDialogOption(
child: Text("Photo With Camera"),
onPressed: handleTakePhoto,
),
SimpleDialogOption(
child: Text("Image from Gallery"),
onPressed: handleChooseFromGallery,
),
SimpleDialogOption(
child: Text("Cancel"),
onPressed: () => Navigator.pop(context),
),
],
);
}
);
}
Container buildSplashScreen(){
return Container(
color: Theme.of(context).accentColor.withOpacity(0.6),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset('assets/images/upload.svg',height: 260.0,),
Padding(
padding: EdgeInsets.only(top:20.0),
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
"Upload Image",
style: TextStyle(
color: Colors.white,
fontSize: 22.0,
),
),
color: Colors.deepOrange,
onPressed: () => selectImage(context),
),
)
],
),
);
}
clearImage(){
setState(() {
file = null;
});
}
//This compresses images for firebase
compressImage() async{
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
Im.Image imageFile = Im.decodeImage(file.readAsBytesSync());
final compressedImageFile = File('$path/img_$postId.jpg')..writeAsBytesSync(Im.encodeJpg(imageFile,quality: 85));
setState(() {
file = compressedImageFile;
});
}
Future<String> uploadImage(imageFile) async{
StorageUploadTask uploadTask = storageRef.child("post_$postId.jpg").putFile(imageFile);
StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
String downloadUrl = await storageSnap.ref.getDownloadURL();
return downloadUrl;
}
//upload new info to firestore that creates a new collection
createPostInFirestore({String mediaUrl, String location, String description}){
postsRef.document(widget.currentUser.id)
.collection("userPosts")
.document(postId)
.setData({
"postId": postId,
"ownerId": widget.currentUser.id,
"username": widget.currentUser.username,
"mediaUrl": mediaUrl,
"description": description,
"location": location,
"timestamp": timeStamp,
"likes":{}
});
}
//Getting the info from the caption, location and pic
handleSubmit() async{
setState(() {
isUploading = true;
});
await compressImage();
String mediaUrl = await uploadImage(file);
createPostInFirestore(
mediaUrl: mediaUrl,
location: locationController.text,
description: captionController.text,
);
captionController.clear();
locationController.clear();
setState(() {
file = null;
isUploading = false;
});
}
Scaffold buildUploadForm(){
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white70,
leading: IconButton(
icon: Icon(Icons.arrow_back,color: Colors.black,),
onPressed: clearImage,
),
title: Text(
"Caption Post",
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
FlatButton(
onPressed: () => isUploading ? null : () => handleSubmit(),
child: Text(
"Post",
style: TextStyle(
color: Colors.blueAccent,
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
),
)
],
),
body: ListView(
children: <Widget>[
isUploading ? linearProgress(context):Text(""),
Container(
height: 220.0,
width: MediaQuery.of(context).size.width*0.8,
child: Center(
child: AspectRatio(
aspectRatio: 16/9,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: FileImage(file),
)
),
),
),
),
),
Padding(
padding: EdgeInsets.only(top:10),
),
ListTile(
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(widget.currentUser.photoUrl),
),
title: Container(
width: 250.0,
child: TextField(
controller: captionController,
decoration: InputDecoration(
hintText: "Write a Caption...",
border: InputBorder.none,
),
),
),
),
Divider(),
ListTile(
leading: Icon(Icons.pin_drop,color: Colors.orange,size: 35.0),
title: Container(
width: 250.0,
child: TextField(
controller: locationController,
decoration: InputDecoration(
hintText: "Where was this photo taken",
border: InputBorder.none,
),
),
),
),
Container(
width: 200.0,
height: 100.0,
alignment: Alignment.center,
child: RaisedButton.icon(
label: Text(
"Use Current Location",
style: TextStyle(color: Colors.white),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
),
color: Colors.blue,
onPressed: () => print("Get user location"),
icon: Icon(
Icons.my_location,
color: Colors.white,
),
),
)
],
),
);
}
#override
Widget build(BuildContext context) {
return file == null ? buildSplashScreen() : buildUploadForm();
}
}
home.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttermedia/models/user.dart';
import 'package:fluttermedia/pages/activity_feed.dart';
import 'package:fluttermedia/pages/create_account.dart';
import 'package:fluttermedia/pages/profile.dart';
import 'package:fluttermedia/pages/search.dart';
import 'package:fluttermedia/pages/upload.dart';
import 'package:google_sign_in/google_sign_in.dart';
final GoogleSignIn googleSignIn = GoogleSignIn();
final StorageReference storageRef = FirebaseStorage.instance.ref();
final usersRef = Firestore.instance.collection('users');
final postsRef = Firestore.instance.collection('posts');
final DateTime timeStamp = DateTime.now();
User currentUser;
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isAuth = false;
PageController pageController;
int pageIndex = 0;
#override
void initState() {
super.initState();
pageController = PageController();
// Detects if user signs in
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err){
print('Error sigining in: $err');
});
//Reauthenticate user when app is opened
googleSignIn.signInSilently(suppressErrors: false)
.then((account) =>
handleSignIn(account)).catchError((err){
print('Error signing in on retry: $err');
});
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
#override
void dispose(){
pageController.dispose();
super.dispose();
}
//Helper Functions
//The sign in section of the code
handleSignIn(GoogleSignInAccount account){
if(account != null){
createUserInFirestore();
setState(() {
isAuth = true;
});
}else{
setState(() {
isAuth = false;
});
}
}
login(){
googleSignIn.signIn();
}
logout(){
googleSignIn.signOut();
}
onPageChanged(int pageIndex){
setState(() {
this.pageIndex = pageIndex;
});
}
createUserInFirestore() async{
// 1) Check if user exists in users collection in database (According to id)
final GoogleSignInAccount user = googleSignIn.currentUser;
DocumentSnapshot doc = await usersRef.document(user.id).get();
if(!doc.exists){
// 2) If the user doesn't exist, take them to create account page
final username = await Navigator.push(context, MaterialPageRoute(builder: (context) => CreateAccount()));
// 3) get username from create account, use it to make new user document in users collection
usersRef.document(user.id).setData({
"id":user.id,
"username":username,
"photoUrl": user.photoUrl,
"email":user.email,
"displayName": user.displayName,
"bio":"",
"timeStamp": timeStamp,
});
doc = await usersRef.document(user.id).get();
}
currentUser = User.fromDocument(doc);
//print(currentUser);
//print(currentUser.username);
}
onTap(int pageIndex){
//This what you would use to animate in between the different screens
pageController.animateToPage(
pageIndex,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut
);
}
//UI Code
Widget buildAuthScreen() {
return Scaffold(
body: PageView(
children: <Widget>[
//Timeline(),
RaisedButton(
child: Text('Logout'),
onPressed: logout,
),
ActivityFeed(),
Upload(currentUser: currentUser),
Search(),
Profile(),
],
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: CupertinoTabBar(
currentIndex: pageIndex,
onTap: onTap,
activeColor: Theme.of(context).primaryColor,
items: [
BottomNavigationBarItem(icon: Icon(Icons.whatshot),),
BottomNavigationBarItem(icon: Icon(Icons.notifications_active),),
BottomNavigationBarItem(icon: Icon(Icons.photo_camera, size: 34.0,),),
BottomNavigationBarItem(icon: Icon(Icons.search),),
BottomNavigationBarItem(icon: Icon(Icons.account_circle),),
],
),
);
/*return RaisedButton(
child: Text('Logout'),
onPressed: logout,
);*/
}
Scaffold buildUnAuthScreen() {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Theme.of(context).primaryColor,
Theme.of(context).accentColor,
]
)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text('FlutterMedia',
style: TextStyle(
fontFamily: "Signatra",
fontSize: 90.0,
color: Colors.white
),
),
GestureDetector(
onTap:() => login(),
child: Container(
width: 260,
height: 60,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/google_signin_button.png'),
fit: BoxFit.cover,
),
),
),
)
],
),
),
);
}
}
onPressed: () => isUploading ? null : () => handleSubmit(),
Well, there's your problem. You meant to have a tertiary condition that makes onPressed call handleSubmit when isUploading is false. Instead, you have made onPressed into a function that returns a function.
To hopefully make that more clear, let's blow this function up into proper non-lambda functions and if/else blocks:
onPressed: () {
if (isUploading) {
return null;
} else {
return () {
handleUpload();
}
}
}
So consider what happens when the button is pressed. It calls the outer function, which checks isUploading. If true, the function returns null, and if false, it returns another function that, if called, calls handleUpload. So how this plays out is that onPressed will never be null (it just returns null sometimes) and handleUpload never gets called (since the inner function that is returned is never then called itself).
Remove the outer lambda and it will work:
onPressed: isUploading ? null : () => handleSubmit(),
Everyone I am new in flutter. How can create loading screen on login page. when i click on login button then it should be redirect on Dashboard with loading screen. I am trying to create loading screen in flutter. When I click on login button then loading screen show be show and after loading screen it should be redirect on Dashboard.....
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutternew/dashboard.dart';
import 'package:flutternew/forget_password.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'bezierContainer.dart';
class Home extends StatefulWidget {
Home({Key key, this.title}) : super(key: key);
final String title;
#override
_HomeState createState() => _HomeState();
}
class ShowLoading extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Loading View'),
),
body: Center(
child: _circularProgressIndicator(),
));
}
Widget _circularProgressIndicator() {
return CircularProgressIndicator();
}
}
class _HomeState extends State<Home> {
bool _isLoading = false;
GlobalKey<FormState> _formKey = GlobalKey<FormState>(debugLabel: '_homekey');
final _scaffoldKey = GlobalKey<ScaffoldState>();
String _email;
String _password;
final TextEditingController emailController = new TextEditingController();
final TextEditingController passwordController = new TextEditingController();
var modal = Container();
showAlertDialog(BuildContext context, String message) {
Widget okButton = FlatButton(
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
);
AlertDialog alert = AlertDialog(
title: Text("Error"),
content: Text(message),
actions: [
okButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Widget _formSetupWidget(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
controller: emailController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Username",
hintStyle: TextStyle(color: Colors.grey, fontSize: 25),
fillColor: Color(0xFFFAFAfA),
filled: true),
keyboardType: TextInputType.emailAddress,
validator: (val) {
if (val.length == 0)
return "Please enter email";
else if (!val.contains("#"))
return "Please enter valid email";
else
return null;
},
onSaved: (val) => _email = val,
),
SizedBox(
height: 15,
),
TextFormField(
controller: passwordController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Password",
hintStyle: TextStyle(color: Colors.grey, fontSize: 25),
fillColor: Color(0xFFFAFAfA),
filled: true),
obscureText: true,
validator: (val) {
if (val.length == 0)
return "Please enter password";
else if (val.length <= 5)
return "Your password should be more then 6 char long";
else
return null;
},
onSaved: (val) => _password = val,
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
),
ButtonTheme(
minWidth: 500.0,
height: 60.0,
child: RaisedButton(
child: new Text("Login",
style: TextStyle(color: Colors.white, fontSize: 23.0)),
color: Colors.blueAccent,
highlightColor: Colors.blueAccent,
onPressed: () {
if (emailController.text != " " ||
passwordController.text != " ") {
signIn(emailController.text, passwordController.text);
}
},
),
),
],
),
);
}
signIn(String email, pass) async {
int visible = 0;
final sharedPreferences = await SharedPreferences.getInstance();
Map data = {'email': email, 'password': pass};
var jsonResponse = null;
var response = await http
.post("https://abcdef.com/iot/api/login", body: data);
if (response.statusCode == 200) {
jsonResponse = json.decode(response.body);
if (jsonResponse != null) {
if (jsonResponse['success'] == 1) {
sharedPreferences.setString("token", jsonResponse['data']['token']);
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) => Dashboard()),
(Route<dynamic> route) => false);
} else {
String message = jsonResponse['message'];
showAlertDialog(context, message);
}
}
} else {
setState(() {
_isLoading = false;
});
print(response.body);
}
}
#override
Widget build(BuildContext context) {
Center(
child: SpinKitWave(color: Colors.white, type: SpinKitWaveType.start),
);
var assetsImage = new AssetImage('assets/logo.png');
var image = new Image(image: assetsImage, width: 150.0, height: 150.0);
final height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
height: height,
color: Colors.white,
child: Stack(
children: <Widget>[
Positioned(
top: -height * .15,
right: -MediaQuery.of(context).size.width * .4,
child: BezierContainer()),
Container(
padding: EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 70),
new Center(
child: image,
),
SizedBox(height: 20),
_formSetupWidget(context),
Container(
padding: EdgeInsets.symmetric(vertical: 10),
alignment: Alignment.centerRight,
child: FlatButton(
child: Text("Forgot Password?"),
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) =>
Forget_password()),
(Route<dynamic> route) => false);
},
),
),
SizedBox(height: height * .055),
],
),
),
),
],
),
));
}
}
Create class
class LoaderDialog {
static Future<void> showLoadingDialog(BuildContext context, GlobalKey key) async {
var wid = MediaQuery.of(context).size.width / 2;
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 130 , right: 130),
child: Dialog(
key: key,
backgroundColor: Colors.white,
child: Container(
width: 60.0,
height: 60.0,
child: Image.asset(
'images/loaderOld.gif',
height: 60,
width: 60,
),
)
),
);
},
);
}
}
How to Call:
In your Class(Where you want to show the loader).
final GlobalKey<State> _LoaderDialog = new GlobalKey<State>();
// Show
LoaderDialog.showLoadingDialog(context, _LoaderDialog);
// Hide
Navigator.of(_LoaderDialog.currentContext,rootNavigator: true).pop();
So I am going to create a quiz app but while I run it, I get the following error
'pakage:flutter/src/wigets/text.dart': Field assertion : line 298 pos
10 :'data !=null': A non-null String must be provided to a Text widget
it is a flutter based code, what are the possible thing i missed ? Can you help me please ! i shall be obliged for this :)
Here Is image of Error
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:crazyquiz/resultpage.dart';
class getjson extends StatelessWidget {
String langname;
getjson(this.langname);
String assettoload;
setasset() {
if (langname == "Funny") {
assettoload = "assets/funny.json";
} else if (langname == "General Knowledge") {
assettoload = "assets/gk.json";
} else if (langname == "Javascript") {
assettoload = "assets/js.json";
} else if (langname == "C++") {
assettoload = "assets/cpp.json";
} else {
assettoload = "assets/linux.json";
}
}
#override
Widget build(BuildContext context) {
setasset();
return FutureBuilder(
future:
DefaultAssetBundle.of(context).loadString(assettoload, cache: true),
builder: (context, snapshot) {
List mydata = json.decode(snapshot.data.toString());
if (mydata == null) {
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
} else {
return quizpage(mydata: mydata);
}
},
);
}
}
class quizpage extends StatefulWidget {
var mydata;
quizpage({Key key, this.mydata = ""}) : super(key: key);
#override
_quizpageState createState() => _quizpageState(mydata);
}
class _quizpageState extends State<quizpage> {
var mydata;
_quizpageState(this.mydata);
Color colortoshow = Colors.indigoAccent;
Color right = Colors.green;
Color wrong = Colors.red;
int marks = 0;
int i = 1;
int j = 1;
int timer = 30;
String showtimer = "30";
var random_array;
Map<String, Color> btncolor = {
"a": Colors.purple,
"b": Colors.lightBlueAccent,
"c": Colors.blueGrey,
"d": Colors.blueAccent,
};
bool canceltimer = false;
genrandomarray() {
var distinctIds = [];
var rand = new Random();
for (int i = 0;;) {
distinctIds.add(rand.nextInt(10));
random_array = distinctIds.toSet().toList();
if (random_array.length < 10) {
continue;
} else {
break;
}
}
print(random_array);
}
#override
void initState() {
starttimer();
genrandomarray();
super.initState();
}
#override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
void starttimer() async {
const onesec = Duration(seconds: 1);
Timer.periodic(onesec, (Timer t) {
setState(() {
if (timer < 1) {
t.cancel();
nextquestion();
} else if (canceltimer == true) {
t.cancel();
} else {
timer = timer - 1;
}
showtimer = timer.toString();
});
});
}
void nextquestion() {
timer = 30;
setState(() {
if (j < 10) {
i = random_array[j];
j++;
} else {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => resultpage(marks: marks),
));
}
btncolor["a"] = Colors.purple;
btncolor["b"] = Colors.lightBlueAccent;
btncolor["c"] = Colors.blueGrey;
btncolor["d"] = Colors.blueAccent;
});
starttimer();
}
void checkanswer(String k) {
if (mydata[2][i.toString()] == mydata[1][i.toString()][k]) {
[i.toString()][k]);
marks = marks + 5;
colortoshow = right;
} else {
}
setState(() {
btncolor[k] = colortoshow;
canceltimer = true;
});
Timer(Duration(seconds: 1), nextquestion);
}
Widget choicebutton(String k) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: MaterialButton(
onPressed: () => checkanswer(k),
child: Text(
mydata[1][i.toString()][k],
style: TextStyle(
color: Colors.white,
fontFamily: "Alike",
fontSize: 16.0,
),
maxLines: 1,
),
color: btncolor[k],
splashColor: Colors.indigo[700],
highlightColor: Colors.indigo[700],
minWidth: 200.0,
height: 45.0,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),
);
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
return WillPopScope(
onWillPop: () {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
"Quizstar",
),
content: Text("You Can't Go Back At This Stage."),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Ok',
),
)
],
));
},
child: Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
mydata[0][i.toString()],
style: TextStyle(
fontSize: 16.0,
fontFamily: "Quando",
),
),
),
),
Expanded(
flex: 6,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
choicebutton('a'),
choicebutton('b'),
choicebutton('c'),
choicebutton('d'),
],
),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.topCenter,
child: Center(
child: Text(
showtimer.toString(),
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.w700,
fontFamily: 'Times New Roman',
),
),
),
),
),
],
),
),
);
}
}`
I think Yalin is perfectly right, but I would add:
Dart has some very useful operators to prevent that kind of errors, which can be catastrofic on real world usage. You can check them here. In particular you could use
mydata[0][i.toString()] ?? "Default text"
to prevent this kind of problem when an object is null
Not a flutter guy. But I'll try to help you.
If you look into flutter source and search your error message you'll get a clue what to do.
So at text.dart we can find that dart check that you fill data field with a String when you call a constructor.
So my bet is that you misplace toString here mydata[0][i.toString()]
Text widget needs a string to be initialized. As vsenik mentioned, you are probably giving a null instead of string to a text widget. The problem is in one of the below lines.
mydata[1][i.toString()][k]
mydata[0][i.toString()]
You could use debug or insert a print statement before these lines.
Hi I have a screen for adding images. I use the multi_image_picker for picking the images. Upon selection of the images, all images picked will be shown in a grid view with a FloatingActionButton on top of each image for removal of each image. The problem is, when I click the button for removing an image, the last image is removed not the one I clicked on. Has anyone experienced this?
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:krakatoa/api/FileApi.dart';
import 'package:krakatoa/components/AssetView.dart';
import 'package:krakatoa/config/Themes.dart' as themes;
import 'package:krakatoa/mixins/SnackBarMixin.dart';
import 'package:krakatoa/podos/User.dart';
import 'package:krakatoa/utils/Common.dart' as common;
import 'package:multi_image_picker/multi_image_picker.dart';
class AddImageScreen extends StatefulWidget {
final List<String> currentImageUrls;
final String postUrl;
AddImageScreen({this.currentImageUrls, #required this.postUrl});
#override
State<StatefulWidget> createState() => _AddImageState();
}
class _AddImageState extends State<AddImageScreen> with SnackBarMixin {
List<Asset> _images = [];
User _user;
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text('Add images'),
),
body: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView(
children: <Widget>[
Container(
padding: EdgeInsets.only(bottom: 10),
child: Text(
"Upload Images:",
style: Theme.of(context).textTheme.headline,
),
),
Container(
padding: EdgeInsets.only(bottom: 10),
child: GridView.count(
shrinkWrap: true,
crossAxisSpacing: 3,
mainAxisSpacing: 3,
crossAxisCount: 3,
children: _renderPickedImages(),
),
),
Container(
child: FlatButton(
child: Text('UPLOAD'),
padding: EdgeInsets.symmetric(vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
color: themes.primaryColor,
onPressed: _onUploadBtnPress,
),
),
],
),
),
);
}
#override
void dispose() {
super.dispose();
for (var image in _images) {
image.release();
}
}
#override
void initState() {
super.initState();
common.getCurrentUser().then((user) {
if (user != null) {
_user = user;
} else {
_user = User();
}
});
}
void showProgressAlert() {
showDialog(
context: context,
builder: (context) {
return WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
content: ListTile(
leading: CircularProgressIndicator(
value: null,
),
title: Text('Processing...'),
),
),
);
},
barrierDismissible: false,
);
}
void _onAddImageBtnPress() async {
List<Asset> resultList;
try {
resultList = await MultiImagePicker.pickImages(maxImages: 5, enableCamera: true);
} on PlatformException catch (e) {
debugPrint("AddImageScreen._onAddImageBtnPress: ${e.toString()}");
}
if (!mounted) return;
if (resultList.isNotEmpty) {
setState(() {
_images.addAll(resultList);
});
}
}
void _onUploadBtnPress() {
if (_images.isNotEmpty) {
showProgressAlert();
_uploadImages();
} else {
showSnackBarMessage("No images to upload", seconds: 5);
}
}
void _removeImage(int index, Asset image) {
debugPrint("INDEX: $index");
debugPrint("Orig Image: ${_images[index].hashCode}");
setState(() {
_images.removeAt(index);
});
}
List<Widget> _renderPickedImages() {
List<Widget> imageWidgets = [];
imageWidgets.add(InkWell(
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.grey,
),
),
child: Center(
child: Icon(
Icons.add,
size: 60,
color: Colors.grey,
),
),
),
onTap: _onAddImageBtnPress,
));
var ctr = 0;
for (var image in _images) {
imageWidgets.add(Container(
child: Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: <Widget>[
AssetView(image),
Positioned(
bottom: 0,
right: 0,
child: _ImageRemoveButton(
index: ctr,
removeItem: _removeImage,
image: image,
),
),
],
),
));
ctr++;
}
return imageWidgets;
}
Future<void> _uploadImages() async {
if (_user.id <= 0) {
showSnackBarMessage("User is not logged in");
Navigator.of(context).pop("User is not logged in");
return;
}
try {
await FileApi.uploadImages(widget.postUrl, _images);
Navigator.of(context).pop();
Navigator.of(context).pop("Success");
} on Exception catch (e) {
debugPrint(e.toString());
showSnackBarMessage(e.toString());
Navigator.of(context).pop(e.toString());
}
}
}
class _ImageRemoveButton extends StatelessWidget {
final Function removeItem;
final Asset image;
final int index;
_ImageRemoveButton({this.removeItem, this.index, this.image});
#override
Widget build(BuildContext context) {
return FloatingActionButton(
backgroundColor: Colors.white,
mini: true,
heroTag: "ImageAction_$index",
isExtended: false,
child: Icon(
Icons.close,
size: 15,
color: Colors.black,
),
onPressed: _onPress,
);
}
void _onPress() {
debugPrint("${this.hashCode}");
debugPrint("Passed Image: ${image.hashCode}");
removeItem(index, image);
}
}
add key: Key(YOUR_LIST.length.toString()),
having KEY helps with the update of the ListView
that worked for me on ListView.builder