What I'm trying to do is allow users to share a post they see on my app. I am using the Flutter Share plugin, but I'm having some difficulty with it and have been looking all over, including the docs which aren't very good.
I have the share button itself working - so the panel will rise up and be displayed to the user. However, it doesn't provide them with any useful option and I can't find how to add those options, such as Facebook, Twitter, Email, Text, etc..
Here is my code, and a picture to show what is happening:
// build method from another class
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Firestore.instance.collection('stories').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_buildEventCards(context, snapshot.data.documents[index]),
);
},
);
}
class ShareButton extends StatelessWidget {
const ShareButton({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text(
'Share',
style: TextStyle(
color: Color.fromRGBO(245, 93, 62, 1.0)),
),
color: Colors.grey[100],
onPressed: () {
final RenderBox box = context.findRenderObject();
Share.share('Hello this is a test',
sharePositionOrigin:
box.localToGlobal(Offset.zero) &
box.size);
},
);
}
}
Sorry the picture is so big, but I would like to add sharing options such as Facebook, Twitter, Email, Messages, etc.
Any help would be much appreciated!
I feel kind of dumb now because I now realize how this Share thing works, but this is definitely something that could be confusing for beginners or people using simulators on their computers wondering what's happening..
Everything works fine the reason I couldn't get anything to show up in the Share panel is because I was testing with simulator app on my computer which doesn't have the mail app, isn't associated with my phone number, isn't logged into Facebook / Twitter, and doesn't have any other apps that could be shared through.
All you have to do is test your app on your actual device and then what ever apps / accounts you're logged into will show up here and you'll be able to share stuff.
Related
Okay, I am sure this is probably an easy to answer question, but after much searching I can't put my finger on the solution.
How does one in Flutter/Dart create a variable in app XYZ that maintains its' value during the time a user moves away from the app, uses another app, allows the phone to go to sleep, etc., then opens the XYZ app again.
I've created a tiny little app that demonstrates the issue as shown below.
The static variable Globals.UID can be incremented upwards, but if the app is moved away from (what term describes using another app correctly?) then as noted the phone goes to sleep, when the user comes back to the app the Globals.UID variable is reset back to -1.
Sometimes the reset is not immediate and I have to let the IOS phone sleep for perhaps 5 minutes to recreate the behavior.
Basically I need Globals.UID to retain its' value until the app is actually exited.
Any insight would truly be appreciated.
import 'package:flutter/material.dart';
import 'package:test_flutter/Globals.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
Globals.UID ++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
Text ( "Global variable is:")
, Text ( Globals.UID.toString() )
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
And in a different file called Globals.dart
class Globals {
static int UID = -1;
}
Thank you!
Ultimately, the solution appears to be solved mostly by using the shared_preferences library. That solution brings with it a good many documented problems, but to maintain data while the app is placed in the background, or even killed by perhaps the user, shared_preferences appears to be the way to go.
Actually this happens to when you don't import with reference. This is a known issues which is already fixed, may be not landed yet. You can check the issue here.
Please try import package_name/globals.dart instead of just import globals.dart if you are doing so.
Hope that helps!
So I have been trying to port my Android and iOS application to Flutter/Dart so I do not have to code everything twice.
I need to display a ListView(that fetches the contents from an array, and then an onClickListener that copies the clicked items content to clipboard.
All fine and good, I have tried two different methods for displaying the ListView from an array and that works fine but I have not gotten the onClickListener to work at all.
Any ideas?
Code like this should work:
class ClipBoardCopier extends StatelessWidget {
#override
Widget build(BuildContext context) {
List<String> data = ['Hello', 'Flutter'];
return ListView.builder(
itemCount: data.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text('ListTile #$index'),
onTap: (){
Clipboard.setData(ClipboardData(text: data[index]));
},
);
},
);
}
}
But if Your ListView Item Widget doesn't have Event Listener (onTap, onClick ..) you can wrap it with GestureDetector And you are good to go.
I am getting started with the BLoC pattern but I have a question:
1) Should you use the BloC pattern to determine if the routes should change?
Example: authentication object changes to unauthenticated so the listeners should handle the route changes.
2) Should the BLoC pattern only be used for UI state and handle the route changes on UI changes?
Example: User clicks on login and navigates to the home screen.
I ask this question because I'm facing a problem where I don't have a central navigation management solution.
This code is in my BLoC now:
loggedIn.listen((AuthResponse user) {
currentUserSubject.add(user);
Navigator.pushReplacement(
_context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) {
return HomePage();
},
transitionsBuilder: (context, animation, _, child) {
return new SlideTransition(
child: child,
position: new Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
);
},
transitionDuration: Duration(milliseconds: 400),
),
);
}, onError: (error) {
Scaffold.of(_context).showSnackBar(new SnackBar(
content: new Text(error.message),
));
});
What I'm currently doing is:
Register a NavigatorBloc that receive a NavigatorState in the constructor and receive actions to navigate to different pages in your app Ex: GoToHomePageAction, GoToRegisterPageAction, NavigatorActionPop.
In your App widget you register the NavigatorBloc and provide the navigator key attached to the MaterialApp widget.
#override
Widget build(BuildContext context) {
return BlocProvider<NavigatorBloc>(
bloc: NavigatorBloc(navigatorKey: widget.navigatorKey),
child: MaterialApp(
navigatorKey: widget.navigatorKey,
title: 'Medicine Tracker',
theme: ThemeData(
primarySwatch: Colors.red,
scaffoldBackgroundColor: Colors.white
),
home: HomePage(),
),
);
}
Then in side your NavigatorBloc you just check the the action and navigate to the desired page:
class NavigatorBloc extends Bloc<NavigatorAction, dynamic>{
final GlobalKey<NavigatorState> navigatorKey;
NavigatorBloc({this.navigatorKey});
#override
dynamic get initialState => 0;
#override
Stream<dynamic> mapEventToState(NavigatorAction event) async* {
if(event is NavigatorActionPop){
yield navigatorKey.currentState.pop();
}
}
}
Hope it help.
I see there are 2 Yes/No questions there (in contrast to W/H questions), and my answers are yes to both of them. The reason being that with BloC and navigation, you can actually recover the current screen if the app crashes (auto-save state and state recover must be in place, but it's another W/H question), and other nice features of BLoC/event-based state management (history snapshot, time machine, event replay, separate of concerns, testability, etc.)
I ask this question because I'm facing a problem where I don't have a central navigation management solution.
Is there anything I can help with regarding your problem?
I want to load an image to cache. So I used CachedNetworkImage for that. When a user logged in through gmail account I get the image url and show the image. But I need to keep it in cache.
Here is my code:
new Center(
child: new Column(
children: <Widget>[
new CircleAvatar(
new CachedNetworkImage(
placeholder: CircularProgressIndicator(),
imageUrl: widget.currentUser?.profilUrl,
),
),
],
),
)
I used CachedNetworkImageProvider also but same error is coming for both. The error is
type'CachedNetworkImage'is not a subtype of type 'ImageProvider<dynamic>'
The widget CircleAvatar receives an ImageProvider.
The cached_network_image package offers you two classes to use:
CachedNetworkImage a Widget you can use to display a cached network image.
CachedNetworkImageProvider an ImageProvider providing the cached image.
Therefore you gotta use CachedNetworkImageProvider(2.), if you want to pass it to the CircleAvatar.
Here is a complete example, that you can copy & paste for trying out:
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
home: Scaffold(
body: Center(
child: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
'https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Bill_Gates_Buys_Skype_%285707954468%29.jpg/2560px-Bill_Gates_Buys_Skype_%285707954468%29.jpg'
),
),
),
)
);
}
}
CircleAvatar backgroundImage property need ImageProvider , Not a Image widget.
ImageProviders :- NetworkImage(), AssetImage() & CachedNetworkImageProvider().
Image Widgets :- Image() , CircleAvatar() , CachedNetworkImage()
So CircleAvatar() & CachedNetworkImage() is also a image displaying widget same as Image() widget, that we used in generally.
Every image displaying widgets need image providers.
So in this case you have to use CircleAvatar() widget with CachedNetworkImageProvider(), because this CachedNetworkImageProvider() provide image data that needs by CircleAvatar() widget.
Just like #Niklas say.
The widget CircleAvatar receives an ImageProvider.
this is how to use:
new Center(
child: new Column(
children: <Widget>[
new CircleAvatar(
backgroundImage: new CachedNetworkImageProvider(
widget.currentUser?.profilUrl,
)
),
],
),
)
I don't know whether it may help someone but he mention how to handle network image error. I usually used Image.Network(Flutter build widget) to handle such errors. this is an example
Image.network(
products[index].image!,
errorBuilder: (BuildContext context,Object exception,StackTrace
stackTrace) {
return Image.asset('img/no_image.png');
},)
the errorbuilder help to show another image in case network image has not been successfully found.
I have a list view on my homepage where if I clicked on it will move to a new page the first one the theme should be blue, and the next one the theme data should be red.
Like how Chat Customization in Facebook's Messenger works.
You should wrap your Page widget inside a Theme widget. Something like below:
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
backgroundColor: Colors.red,
),
child: Builder(
builder: (context) {
return YourPageWidget(context);
}
),
);
}
There is one thing you should aware of. Using a Builder() to pass the latest context (including your custom theme data in this new context) to the child widget.
If not using Builder, in some cases, YourPageWidget can't get correct theme data with command Theme.of(context)...
For more information, you can reference the document Create unique ThemeData