Adding a Chip to a Material TextField - dart

Say we created a Chip object and TextField object like below. How do you add a Chip to the inside of the TextField?
new Chip(
label: new Text('Peyton Smith'),
)
new TextField(
)
Is it possible to combine them to get something like in the Material spec where typing in something into a Material TextField adds a Chip?

What you are looking for was actually provided in the comment.
Here is a simple implementation using InputChip:
InputChip(
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text('AB'),
),
label: Text('Aaron Burr'),
onPressed: () {
print('I am the one thing in life.');
}
)
A material design input chip.
Input chips represent a complex piece of information, such as an
entity (person, place, or thing) or conversational text, in a compact
form.
Input chips can be made selectable by setting
onSelected,
deletable by setting
onDeleted,
and pressable like a button with
onPressed.
They have a
label,
and they can have a leading icon (see
avatar)
and a trailing icon
(deleteIcon).
Colors and padding can be customized.
Here is my simple interpretation of InputChip:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: _toContainer(),
),
),
Divider(
color: Colors.blueGrey,
height: 10.0,
),
Align(
alignment: Alignment.centerLeft,
child: _subjectContainer(),
),
Divider(
color: Colors.blueGrey,
height: 10.0,
),
Align(
alignment: Alignment.centerLeft,
child: _messageContainer(),
),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _messageContainer() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Text(
'Message',
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
),
);
}
Widget _toContainer() {
return Wrap(
spacing: 5.0,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8.0, right: 8.0),
child: Container(
child: Text(
'To',
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
),
),
Container(
child: _profileChips("Scott Hill",
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80"),
),
],
);
}
Widget _subjectContainer() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Text(
'Subject',
style: TextStyle(color: Colors.black, fontSize: 18.0),
),
),
);
}
Widget _profileChips(String myName, String myImage) {
return Material(
child: InputChip(
avatar: CircleAvatar(
backgroundColor: Colors.blueGrey,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(myImage),
)),
),
),
label: Text(myName),
labelStyle: TextStyle(
color: Colors.black, fontSize: 14.0, fontWeight: FontWeight.bold),
onPressed: () {},
onDeleted: () {},
),
);
}
}
Output:
And for a fully functional example, I've tested the answer in here.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// See: https://twitter.com/shakil807/status/1042127387515858949
// https://github.com/pchmn/MaterialChipsInput/tree/master/library/src/main/java/com/pchmn/materialchips
// https://github.com/BelooS/ChipsLayoutManager
void main() => runApp(ChipsDemoApp());
class ChipsDemoApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.indigo,
accentColor: Colors.pink,
),
home: DemoScreen(),
);
}
}
class DemoScreen extends StatefulWidget {
#override
_DemoScreenState createState() => _DemoScreenState();
}
class _DemoScreenState extends State<DemoScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Material Chips Input'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: const InputDecoration(hintText: 'normal'),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ChipsInput<AppProfile>(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search), hintText: 'Profile search'),
findSuggestions: _findSuggestions,
onChanged: _onChanged,
chipBuilder: (BuildContext context,
ChipsInputState<AppProfile> state, AppProfile profile) {
return InputChip(
key: ObjectKey(profile),
label: Text(profile.name),
avatar: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
onDeleted: () => state.deleteChip(profile),
onSelected: (_) => _onChipTapped(profile),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
);
},
suggestionBuilder: (BuildContext context,
ChipsInputState<AppProfile> state, AppProfile profile) {
return ListTile(
key: ObjectKey(profile),
leading: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
title: Text(profile.name),
subtitle: Text(profile.email),
onTap: () => state.selectSuggestion(profile),
);
},
),
),
),
],
),
);
}
void _onChipTapped(AppProfile profile) {
print('$profile');
}
void _onChanged(List<AppProfile> data) {
print('onChanged $data');
}
Future<List<AppProfile>> _findSuggestions(String query) async {
if (query.length != 0) {
return mockResults.where((profile) {
return profile.name.contains(query) || profile.email.contains(query);
}).toList(growable: false);
} else {
return const <AppProfile>[];
}
}
}
// -------------------------------------------------
const mockResults = <AppProfile>[
AppProfile('Stock Man', 'stock#man.com',
'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
AppProfile('Paul', 'paul#google.com',
'https://mbtskoudsalg.com/images/person-stock-image-png.png'),
AppProfile('Fred', 'fred#google.com',
'https://media.istockphoto.com/photos/feeling-great-about-my-corporate-choices-picture-id507296326'),
AppProfile('Bera', 'bera#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('John', 'john#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Thomas', 'thomas#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Norbert', 'norbert#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Marina', 'marina#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
];
class AppProfile {
final String name;
final String email;
final String imageUrl;
const AppProfile(this.name, this.email, this.imageUrl);
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppProfile &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
#override
String toString() {
return 'Profile{$name}';
}
}
// -------------------------------------------------
typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query);
typedef ChipSelected<T> = void Function(T data, bool selected);
typedef ChipsBuilder<T> = Widget Function(
BuildContext context, ChipsInputState<T> state, T data);
class ChipsInput<T> extends StatefulWidget {
const ChipsInput({
Key key,
this.decoration = const InputDecoration(),
#required this.chipBuilder,
#required this.suggestionBuilder,
#required this.findSuggestions,
#required this.onChanged,
this.onChipTapped,
}) : super(key: key);
final InputDecoration decoration;
final ChipsInputSuggestions findSuggestions;
final ValueChanged<List<T>> onChanged;
final ValueChanged<T> onChipTapped;
final ChipsBuilder<T> chipBuilder;
final ChipsBuilder<T> suggestionBuilder;
#override
ChipsInputState<T> createState() => ChipsInputState<T>();
}
class ChipsInputState<T> extends State<ChipsInput<T>>
implements TextInputClient {
static const kObjectReplacementChar = 0xFFFC;
Set<T> _chips = Set<T>();
List<T> _suggestions;
int _searchId = 0;
FocusNode _focusNode;
TextEditingValue _value = TextEditingValue();
TextInputConnection _connection;
String get text => String.fromCharCodes(
_value.text.codeUnits.where((ch) => ch != kObjectReplacementChar),
);
bool get _hasInputConnection => _connection != null && _connection.attached;
void requestKeyboard() {
if (_focusNode.hasFocus) {
_openInputConnection();
} else {
FocusScope.of(context).requestFocus(_focusNode);
}
}
void selectSuggestion(T data) {
setState(() {
_chips.add(data);
_updateTextInputState();
_suggestions = null;
});
widget.onChanged(_chips.toList(growable: false));
}
void deleteChip(T data) {
setState(() {
_chips.remove(data);
_updateTextInputState();
});
widget.onChanged(_chips.toList(growable: false));
}
#override
void initState() {
super.initState();
_focusNode = FocusNode();
_focusNode.addListener(_onFocusChanged);
}
void _onFocusChanged() {
if (_focusNode.hasFocus) {
_openInputConnection();
} else {
_closeInputConnectionIfNeeded();
}
setState(() {
// rebuild so that _TextCursor is hidden.
});
}
#override
void dispose() {
_focusNode?.dispose();
_closeInputConnectionIfNeeded();
super.dispose();
}
void _openInputConnection() {
if (!_hasInputConnection) {
_connection = TextInput.attach(this, TextInputConfiguration());
_connection.setEditingState(_value);
}
_connection.show();
}
void _closeInputConnectionIfNeeded() {
if (_hasInputConnection) {
_connection.close();
_connection = null;
}
}
#override
Widget build(BuildContext context) {
var chipsChildren = _chips
.map<Widget>(
(data) => widget.chipBuilder(context, this, data),
)
.toList();
final theme = Theme.of(context);
chipsChildren.add(
Container(
height: 32.0,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
text,
style: theme.textTheme.subtitle1.copyWith(
height: 1.5,
),
),
_TextCaret(
resumed: _focusNode.hasFocus,
),
],
),
),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
//mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: requestKeyboard,
child: InputDecorator(
decoration: widget.decoration,
isFocused: _focusNode.hasFocus,
isEmpty: _value.text.length == 0,
child: Wrap(
children: chipsChildren,
spacing: 4.0,
runSpacing: 4.0,
),
),
),
Expanded(
child: ListView.builder(
itemCount: _suggestions?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
return widget.suggestionBuilder(
context, this, _suggestions[index]);
},
),
),
],
);
}
#override
void updateEditingValue(TextEditingValue value) {
final oldCount = _countReplacements(_value);
final newCount = _countReplacements(value);
setState(() {
if (newCount < oldCount) {
_chips = Set.from(_chips.take(newCount));
}
_value = value;
});
_onSearchChanged(text);
}
int _countReplacements(TextEditingValue value) {
return value.text.codeUnits
.where((ch) => ch == kObjectReplacementChar)
.length;
}
#override
void performAction(TextInputAction action) {
_focusNode.unfocus();
}
void _updateTextInputState() {
final text =
String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));
_value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
composing: TextRange(start: 0, end: text.length),
);
_connection.setEditingState(_value);
}
void _onSearchChanged(String value) async {
final localId = ++_searchId;
final results = await widget.findSuggestions(value);
if (_searchId == localId && mounted) {
setState(() => _suggestions = results
.where((profile) => !_chips.contains(profile))
.toList(growable: false));
}
}
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _TextCaret extends StatefulWidget {
const _TextCaret({
Key key,
this.duration = const Duration(milliseconds: 500),
this.resumed = false,
}) : super(key: key);
final Duration duration;
final bool resumed;
#override
_TextCursorState createState() => _TextCursorState();
}
class _TextCursorState extends State<_TextCaret>
with SingleTickerProviderStateMixin {
bool _displayed = false;
Timer _timer;
#override
void initState() {
super.initState();
_timer = Timer.periodic(widget.duration, _onTimer);
}
void _onTimer(Timer timer) {
setState(() => _displayed = !_displayed);
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return FractionallySizedBox(
heightFactor: 0.7,
child: Opacity(
opacity: _displayed && widget.resumed ? 1.0 : 0.0,
child: Container(
width: 2.0,
color: theme.primaryColor,
),
),
);
}
}
Working output:
Aside from the samples above, you have the option to use flutter_chips_input plugin.
Flutter library for building input fields with InputChips as input
options.
Here is an example:

Related

Unhandled Exception: Bad state: No element

My build was working fine until I added an image to the launch screen, when I started getting a PhaseScript error. I had to completely delete the ios folder, recreate it, and then make the necessary manual changes again. Now when I run the build on a simulator through android studio, I get this error once the app starts:
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: Bad state: No element
#0 List.first (dart:core-patch/growable_array.dart:339:5)
#1 main (package:globe_flutter/main.dart:19:25)
<asynchronous suspension>
Nothing shows up on the screen. I use a tabBar in the main.dart and a tabBarView to show the different screens. Here is the main.dart:
var firstCamera;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
firstCamera = cameras.first;
await Firebase.initializeApp();
runApp(MyApp());
}
// void main() async {
// WidgetsFlutterBinding.ensureInitialized();
// await Firebase.initializeApp();
// runApp(MyApp());
// }
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GLOBE',
debugShowCheckedModeBanner: false,
home: const MyHome(),
routes: {
LoginScreen.id: (context) => LoginScreen(),
SignupScreen.id: (context) => SignupScreen(),
HomeScreen.id: (context) => HomeScreen()
},
);
}
}
class MyHome extends StatefulWidget {
const MyHome({Key? key}) : super(key: key);
#override
_MyHomeState createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> with SingleTickerProviderStateMixin {
late TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(length: 5, vsync: this);
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: TabBarView(
children: [
HomeScreen(),
HomeScreen(),
TakePictureScreen(camera: firstCamera),
HomeScreen(),
ProfileScreen(username: "cjmofficial")
],
controller: _tabController
),
extendBody: true,
bottomNavigationBar: Container(
color: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: 40, horizontal: 40),
child: ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(50.0),
child: Container(
height: 55,
color: Colors.grey[200],
child: TabBar(
labelColor: Colors.teal[200],
unselectedLabelColor: Colors.blueGrey,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(color: Colors.teal),
insets: EdgeInsets.fromLTRB(50, 0, 50, 40)
),
indicatorColor: Colors.teal,
tabs: [
Tab(icon: Icon(Icons.home_outlined)),
Tab(icon: Icon(Icons.explore_outlined)),
Tab(icon: Icon(Icons.camera_alt_outlined)),
Tab(icon: Icon(Icons.movie_outlined)),
Tab(icon: Icon(Icons.person_outline))
],
controller: _tabController),
),
),
),
);
}
}
and the first screen that is supposed to show in the TabBarView:
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
static const String id = "home_screen";
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Color _likeButtonColor = Colors.black;
Widget _buildPost(String username, String imageUrl, String caption, String pfpUrl) {
return Container(
color: Colors.white,
child: Column(
children: [
GestureDetector(
onTap: (){},
child: Container(
height: 50,
color: Colors.deepOrangeAccent[100],
child: Row(
children: [
SizedBox(width: 5),
CircleAvatar(
child: ClipOval(
child: Image.network(
pfpUrl,
height: 40,
width: 40,
),
),
),
SizedBox(width: 10),
Icon(Icons.more_vert, size: 20),
SizedBox(width: 10),
Text(username, style: TextStyle(fontSize: 15))
],
),
),
),
Stack(
children: [
Image.asset("images/post_background.jpg"),
Padding(
padding: const EdgeInsets.all(20.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.network(imageUrl, fit: BoxFit.cover)),
),
],
),
Container(
height: 100,
child: Column(
children: [
const SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
setState(() {
HapticFeedback.lightImpact();
});
},
icon: Icon(Icons.thumb_up_alt_outlined, size: 30)),
Text("l", style: TextStyle(fontSize: 30)),
IconButton(
onPressed: () {
setState(() {
HapticFeedback.lightImpact();
GallerySaver.saveImage(imageUrl);
});
},
icon: Icon(Icons.ios_share, size: 30))
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(caption, style: const TextStyle(fontSize: 15))
],
)
],
),
)
],
),
);
}
List<Post> listPosts = [];
fetchPosts() async {
final userRef = FirebaseFirestore.instance.collection('users');
final QuerySnapshot result = await userRef.get();
result.docs.forEach((res) async {
print(res.id);
QuerySnapshot posts = await userRef.doc(res.id).collection("posts").get();
posts.docs.forEach((res) {
listPosts.add(Post.fromJson(res.data() as Map<String, dynamic>));
});
// Other method
// listPosts = posts.docs.map((doc) => Post.fromJson(doc.data() as Map<String, dynamic>)).toList();
setState(() {});
});
setState(() {
listPosts.sort((a, b) => a.postedDate.compareTo(b.postedDate));
});
}
pfpUrl(String username) async {
// Get pfpUrl
String downloadURL = await FirebaseStorage.instance
.ref(username + "/profile_picture.png")
.getDownloadURL();
return downloadURL;
}
#override
void initState() {
fetchPosts();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(45.0),
child: AppBar(
leading:
Icon(Icons.monetization_on_outlined, color: Colors.blueGrey),
centerTitle: true,
title: Text("GLOBE", style: TextStyle(color: Colors.blueGrey)),
actions: [
Icon(Icons.search, color: Colors.blueGrey),
SizedBox(width: 10)
],
backgroundColor: Colors.tealAccent[100]),
),
body: ListView.builder(
itemCount: listPosts.length,
itemBuilder: (BuildContext context, int index) {
// We retrieve the post at index « index »
final post = listPosts[index];
// Get name from id
var parts = post.id.split('_');
var username = parts[0].trim();
// Get pfpUrl
String pfpUrlString = "https://play-lh.googleusercontent.com/IeNJWoKYx1waOhfWF6TiuSiWBLfqLb18lmZYXSgsH1fvb8v1IYiZr5aYWe0Gxu-pVZX3";
return _buildPost(username, post.postUrlString, post.caption, pfpUrlString);
}),
);
}
}
I'm new to flutter and all I can understand about this is that something cannot load. I also ran a build with verbose but it didn't give anymore information. If someone can help me understand the issue, that would be great.
Ios simulators do not have cameras so on line 19, when firstCamera = cameras.first is called, there is no first camera.

Flutter: how to pass an image file to another screen

Hi I need some help with passing an image that is selected from my gallery to another screen.
When I go to the 'Pick Image' screen through the grid-tiled screen, I can select an image from my gallery app. As soon as I pick one, the image pops up on the 'Pick Image' screen and it should be passed to the previous screen(grid tiled) so the image can be displayed in a grid tile.
Anyone could handle this?
The relevant part of the code is right below.
Grid Tile screen
Widget build(BuildContext context) {
return GridView.count(crossAxisCount: 4,
children: List.generate(lastDay, (index){
return GridTile(
child: Card(
child: Column(
children: <Widget>[
Text('Day ' '$index'),
SizedBox(height: 20.0,),
IconButton(
icon: Icon(Icons.add),
onPressed: (){
Navigator.push(context,
MaterialPageRoute(builder: (context) => PickImage()));
},),
],
),
),
);
}),
);
}
Pick Image Screen
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class PickImage extends StatefulWidget {
PickImage() : super();
final String title = "Pick Image";
#override
_PickImageState createState() => _PickImageState();
}
class _PickImageState extends State<PickImage> {
Future<File> imageFile;
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
width: 400,
height: 400,
);
} else if (snapshot.error != null) {
return const Text(
'Error Picking Image',
textAlign: TextAlign.center,
);
} else {
return const Text(
'No Image Selected',
textAlign: TextAlign.center,
);
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
showImage(),
RaisedButton(
child: Text("Select Image from Gallery"),
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
},
),
],
),
),
);
}
}
You can copy paste run full code below
Step 1: Use Navigator.pop to return image
RaisedButton(
onPressed: () {
Navigator.pop(context, imageFileReturn);
},
child: Text('Selecct Finish, Go back '),
),
Step 2: Use Map<int, File> keep related index and image
Map<int, File> imageFileMap = {};
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
imageFile = await Navigator.push(context,
MaterialPageRoute(builder: (context) => PickImage()));
imageFileMap[index] = imageFile;
setState(() {});
},
Step 3: Show image in Map
SizedBox(
height: 20.0,
child: imageFileMap[index] != null
? Image.file(
imageFileMap[index],
)
: Container()),
working demo
full code
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class PickImage extends StatefulWidget {
PickImage() : super();
final String title = "Pick Image";
#override
_PickImageState createState() => _PickImageState();
}
class _PickImageState extends State<PickImage> {
Future<File> imageFile;
File imageFileReturn;
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
imageFileReturn = snapshot.data;
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
width: 400,
height: 400,
);
} else if (snapshot.error != null) {
return const Text(
'Error Picking Image',
textAlign: TextAlign.center,
);
} else {
return const Text(
'No Image Selected',
textAlign: TextAlign.center,
);
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
showImage(),
RaisedButton(
child: Text("Select Image from Gallery"),
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
},
),
RaisedButton(
onPressed: () {
Navigator.pop(context, imageFileReturn);
},
child: Text('Selecct Finish, Go back '),
),
],
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Test(),
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
int lastDay = 30;
Map<int, File> imageFileMap = {};
File imageFile;
#override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 4,
children: List.generate(lastDay, (index) {
return GridTile(
child: Card(
child: Column(
children: <Widget>[
Text('Day ' '$index'),
SizedBox(
height: 20.0,
child: imageFileMap[index] != null
? Image.file(
imageFileMap[index],
)
: Container()),
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
imageFile = await Navigator.push(context,
MaterialPageRoute(builder: (context) => PickImage()));
imageFileMap[index] = imageFile;
setState(() {});
},
),
],
),
),
);
}),
);
}
}

Bloc pattern update ui with listener or stream

in my implementation Bloc pattern i can get some data from server like with name and email, i can show this data with separated widget in layout such as show name with Text, as i have some widget in layout such as
Text('Hi $_username')
before getting data from server i want to try update this variable after getting data from server
for example:
class Login extends StatefulWidget {
final LoginListingBloc loginListingBloc;
Login({this.loginListingBloc});
State<Login> createState() {
return _Login(loginListingBloc: loginListingBloc);
}
}
class _Login extends State<Login> {
var _userPageName='';
_Login({this.loginListingBloc});
final LoginListingBloc loginListingBloc;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 50.0),
child: SingleChildScrollView(
child: Container(
...child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"welcome $_userPageName",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600),
),
...
],
)),
Row(
children: <Widget>[
...
IconButton(
icon: new Icon(Icons.account_circle),
onPressed: () {
loginListingBloc.dispatch(LoginEvent(loginInfoModel: testLogin));
},
iconSize: 40.0,
color: Colors.white,
),
],
),
),
],
),
LoginListing()
],
)),
),
)
],
),
);
}
}
class LoginListing extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: BlocProvider.of<LoginListingBloc>(context),
builder: (context, state) {
if (state is LoginUninitializedState) {
return Message(
message: 'login is not available',
);
} else if (state is LoginFetchingState) {
return Expanded(
child: CircularProgressIndicator(),
);
} else if (state is LoginErrorState) {
return Message(message: 'Error');
} else {
final stateAsLoginFetchedState = state as LoginFetchedState;
final loginInfo = stateAsLoginFetchedState.userInfo;
return buildShowLoginInfoItems(loginInfo);
}
});
}
Widget buildShowLoginInfoItems(UserInfo userInfo) {
return Container(
child: Text(userInfo.name),
);
}
}
in that how can i update $_userPageName after click on IconButton?
my LoginListingBloc implemented class:
class LoginListingBloc extends Bloc<LoginListingEvent, LoginListingStates> {
final LoginRepository loginRepository;
LoginListingBloc({this.loginRepository});
#override
LoginListingStates get initialState => LoginUninitializedState();
#override
Stream<LoginListingStates> mapEventToState(
LoginListingStates currentState, LoginListingEvent event) async* {
if (event is LoginEvent) {
yield LoginFetchingState();
try {
final loginInfo = await loginRepository.fetchLoginToPage(
event.loginInfoModel.username, event.loginInfoModel.password);
yield LoginFetchedState(userInfo: loginInfo);
} catch (_) {
yield LoginErrorState();
}
}
}
}

How to pass down data from Stateful classes to another Stateful class that exists in another file?

I'm having trouble passing the data that's been filled in a textformfields and selected in a dropdown menu.
I'm trying to use the Map function to pass down String values so that I can also pass down all types of values in the future (ex. int, bool, double etc.), however it's not working so I need someone to check it out.
main.dart
import 'package:flutter/material.dart';
import 'package:workoutapp/auth/auth.dart';
import 'package:workoutapp/auth/root_page.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';
void main(List<String> args) {
runApp(
WorkoutManager(),
);
}
class WorkoutManager extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AuthProvider(
auth: Auth(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Workout Manager',
home: RootPage(),
theme: ThemeData(
primaryColor: Colors.indigo,
primarySwatch: Colors.indigo,
accentColor: Colors.indigoAccent,
hintColor: Colors.indigo,
brightness: Brightness.dark,
),
),
);
}
}
HomePage
import 'package:flutter/material.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';
import './profile_account_page.dart';
import './routines_create_page.dart';
import '../objects/Routines/routines_manager.dart';
import '../tools/custom_drawer.dart';
class HomePage extends StatelessWidget {
final VoidCallback onSignedOut;
final List<Map<String, String>> routines;
HomePage({Key key, this.onSignedOut, this.routines}) : super(key: key);
void _signedOut(BuildContext context) async {
try {
var auth = AuthProvider.of(context).auth;
await auth.signOut();
onSignedOut();
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Workout Manager', style: TextStyle(color: Colors.white)),
centerTitle: false,
actions: <Widget>[
FlatButton(
child: Text('Logout'),
onPressed: () {
return _signedOut(context);
},
),
IconButton(
icon: Icon(Icons.account_box),
tooltip: 'Profile Account',
color: Colors.white,
onPressed: () {
return Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return ProfileAccountPage();
}));
},
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return RoutinesPageCreate();
}));
},
),
body: RoutinesManager(),
drawer: CustomDrawer(),
);
}
}
RoutineManager
import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines.dart';
class RoutinesManager extends StatefulWidget {
final Map<String, String> startingRoutine;
RoutinesManager({this.startingRoutine});
#override
_RoutinesManagerState createState() => _RoutinesManagerState();
}
class _RoutinesManagerState extends State<RoutinesManager> {
List<Map<String, String>> _routines = [];
#override
void initState() {
if (widget.startingRoutine != null) {
_routines.add(widget.startingRoutine);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Routines(_routines),
)
],
);
}
}
RoutinesCreatePage
import 'package:flutter/material.dart';
import 'package:workoutapp/pages/home_page.dart';
class RoutinesPageCreate extends StatefulWidget {
#override
_RoutinesPageCreateState createState() => _RoutinesPageCreateState();
}
class _RoutinesPageCreateState extends State<RoutinesPageCreate> {
final formKey = GlobalKey<FormState>();
List<Map<String, String>> _routines = [];
String _routineName, _routineDescription;
var _routineNameController = TextEditingController();
var _routineDescriptionController = TextEditingController();
List<DropdownMenuItem<String>> _dropdownListBodyPartMenuItem = [];
List<String> _dropdownListBodyPart = [
'Chest',
'Back',
'Leg',
'Shoulder',
'Abs',
];
String _selectedBodyPart;
List<DropdownMenuItem<String>> _dropdownListDayOfWeekMenuItem = [];
List<String> _dropdownListDayOfWeek = [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
];
String _selectedDayOfWeek;
void loadBodyPartData() {
_dropdownListBodyPartMenuItem = [];
_dropdownListBodyPartMenuItem = _dropdownListBodyPart.map((val) {
return DropdownMenuItem<String>(
child: Text(val),
value: val,
);
}).toList();
}
void loadDayOfWeekData() {
_dropdownListDayOfWeekMenuItem = [];
_dropdownListDayOfWeekMenuItem = _dropdownListDayOfWeek.map((val) {
return DropdownMenuItem<String>(
child: Text(val),
value: val,
);
}).toList();
}
final _scaffoldState = GlobalKey<ScaffoldState>();
void _showSnakBarReset() {
_scaffoldState.currentState.showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).accentColor,
content: Text('Showing SnackBar TEST'),
),
);
}
void _showSnakBarCreateWorkoutRoutine() {
_scaffoldState.currentState.showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).accentColor,
content: Text('Workout Routine has been created'),
),
);
}
void _addRoutine(Map<String, String> routine) {
setState(() {
_routines.add(routine);
});
}
#override
Widget build(BuildContext context) {
loadBodyPartData();
loadDayOfWeekData();
return Scaffold(
key: _scaffoldState,
appBar: AppBar(
title: Text('Create Routines'),
),
body: Container(
padding: EdgeInsets.all(15.0),
child: Form(
key: formKey,
child: ListView(children: buildInputs() + buildCreateButtons()),
),
),
);
}
List<Widget> buildInputs() {
TextStyle textStyle = Theme.of(context).textTheme.title;
return [
TextFormField(
controller: _routineNameController,
validator: (value) {
if (value.length > 20) {
return 'Not a valid Routine Name';
}
},
onSaved: (value) {
return _routineName = value;
},
decoration: InputDecoration(
labelStyle: textStyle,
labelText: 'Routine Name',
hintText: 'Enter the Routine Name for this day',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
))),
Padding(padding: EdgeInsets.all(7.0)),
TextFormField(
controller: _routineDescriptionController,
validator: (value) {
if (value.length > 50) {
return 'Invalid: The Description must be 50 characters or less.';
}
},
onSaved: (value) {
return _routineDescription = value;
},
decoration: InputDecoration(
labelStyle: textStyle,
labelText: 'Description',
hintText: 'Enter the description of the Routine.',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
))),
Padding(padding: const EdgeInsets.all(7.0)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DropdownButtonHideUnderline(
child: DropdownButton(
value: _selectedBodyPart,
items: _dropdownListBodyPartMenuItem,
hint: Text('Select Body Part', style: textStyle),
onChanged: (value) {
setState(() {
_selectedBodyPart = value;
});
})),
Padding(
padding: const EdgeInsets.all(1.0),
),
DropdownButtonHideUnderline(
child: DropdownButton(
value: _selectedDayOfWeek,
items: _dropdownListDayOfWeekMenuItem,
hint: Text('Select Day of Week', style: textStyle),
onChanged: (value) {
setState(() {
_selectedDayOfWeek = value;
});
},
),
),
Padding(
padding: const EdgeInsets.all(4.0),
)
],
),
];
}
List<Widget> buildCreateButtons() {
return [
Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
textColor: Theme.of(context).primaryColorDark,
color: Theme.of(context).accentColor,
child: Text('Create Workout Routine'),
onPressed: () {
if (formKey.currentState.validate()) {
_showSnakBarCreateWorkoutRoutine();
formKey.currentState.save();
_addRoutine({
'routineName': 'Chest Workout',
'description': 'Heavy',
'bodyPart': 'Chest',
'week': 'Monday',
});
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return HomePage();
}));
} else {
return null;
}
}),
),
Expanded(
child: RaisedButton(
textColor: Theme.of(context).primaryColorLight,
color: Theme.of(context).primaryColorDark,
child: Text('Reset'),
onPressed: () {
setState(() {
_showSnakBarReset();
formKey.currentState.reset();
_selectedBodyPart = null;
_selectedDayOfWeek = null;
});
},
),
),
],
),
),
];
}
}
Routines
import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines_detail.dart';
class Routines extends StatelessWidget {
final List<Map<String, String>> routines;
Routines(this.routines);
Widget _buildRoutinesItem(BuildContext context, int index) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Expanded(
child: Card(
margin: EdgeInsets.all(5.0),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['routineName'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['description'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['bodyPart'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['week'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
child: Text('Details'),
onPressed: () {
return Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return RoutinesDetail(
routines[index]['routineName'],
routines[index]['description'],
routines[index]['bodyPart'],
routines[index]['week']);
}));
},
)
],
),
)
],
),
),
);
}
Widget _buildRoutinesList(context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
Widget routinesCards = Container(
child: Container(
child: Center(
child: Text("No routines found, please add some.", style: textStyle),
),
),
);
if (routines.length > 0 || routines.length <= 7) {
ListView.builder(
itemBuilder: _buildRoutinesItem,
itemCount: routines.length,
);
}
return routinesCards;
}
#override
Widget build(BuildContext context) {
return _buildRoutinesList(context);
}
}
RoutineDetailPage
import 'package:flutter/material.dart';
class RoutinesDetail extends StatelessWidget {
final String routineName, description, bodyPart, week;
RoutinesDetail(this.routineName, this.description, this.bodyPart, this.week);
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Scaffold(
appBar: AppBar(
title: Text(routineName),
centerTitle: true,
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routineName, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(description, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(bodyPart, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(week, style: textStyle)),
Container(
padding: EdgeInsets.all(5.0),
child: RaisedButton(
child: Text('Delete'),
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
),
);
}
}
As you can see, I'm trying to separate the code into multiple files as much as possible, so it's more "readable" and make it easy for myself to make changes to the code whenever I have to in the future.
The problem is, it's quite spit up, I don't understand how to use the data and pass it down or up to the pages or widgets as there are multiple stateful and stateless widgets that are suppose to work together to make this app possible.
You'll notice on the HomePage file (StatelessWidget), I'm trying to show the Scaffold body argument with the RoutinesManager StatefulWidget, which is in a different file. At the same time in the HomePage file, I have a Scaffold floatingActionButton argument that will take you to the RoutinesCreatePage StatefulWidget to create a List of Cards (StatelessWidget) using the ListView.builder(). However, no Card gets created under the HomePage after the "Create Workout Routine" RaisedButton gets pressed in the RoutinesCreatePage and no data gets passed.
Can someone please help me out here as I am totally clueless. Also, I'm fairly a beginner regarding flutter/dart so a solution with a relatively easy to understand explanation would be very helpful.
Note: I do have other files that contribute to this app, however I don't think they're part of the problem so I left them out intentionally.
If more information is needed, please do let me know.
Thanks you!
it looks like you misunderstand what state in Flutter is. To explain in short, state is the internal status/data/... that belongs that that specific widget. StatefulWidget has state to determine if UI should be re-rendered on its own state change. External widgets never know about other widgets' states.
So it means, any state change happening inside RoutinesCreatePage widget, only that RoutinesCreatePage knows and reacts. Unless, you inform other widgets to know something has changed.
Alright, so talking about navigation, it works like a stack structure. HomePage trigger a push to RoutinesCreatePage, then to return, you need to pop, not another push.
Here a quick fix for your code, you can try.
HomePage
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
handleNewRoutine(); <--- this is to handle navigation and retrieve returning data from pop
},
),
Future handleNewRoutine() async {
var newRoutine = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => RoutinesPageCreate());
)
if (newRoutine == null) {
// nothing returns from RoutinesPageCreate widget
// so do nothing then
} else {
// add to routine list
// and trigger list re-rendering
setState(() {
this.routines.add(newRoutine);
});
}
}
RoutinesCreatePage: when clicking submit button, just populate all data from input fields, make object model and pop to return data to where this widget was pushed.
onPressed: () {
var newRoutine = .... // populate from UI to create new Routine model object.
Navigator.pop(context, newRoutine);
}
Also, take time to read the navigation guide from official Flutter documentation. It is very detailed on this part. https://flutter.io/docs/cookbook/navigation/returning-data
Some additional comments to your code:
in RoutinesCreatePage you don't need to know application level state, I mean _routines variable is unnecessary. You only need one object to store new routine to pop back to HomePage.
in Routines, this method Widget _buildRoutinesList(context) having unused ListView creation.
if (routines.length > 0 || routines.length <= 7) {
ListView.builder(
itemBuilder: _buildRoutinesItem,
itemCount: routines.length,
);
}

Check if an application is on its first run with Flutter

Basically, I want to have a screen/view that will open when the user opens up the app for the first time. This will be a login screen type of thing.
Use Shared Preferences Package. You can read it with FutureBuilder, and you can check if there is a bool named welcome for example. This is the implementation I have in my code:
return new FutureBuilder<SharedPreferences>(
future: SharedPreferences.getInstance(),
builder:
(BuildContext context, AsyncSnapshot<SharedPreferences> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new LoadingScreen();
default:
if (!snapshot.hasError) {
#ToDo("Return a welcome screen")
return snapshot.data.getBool("welcome") != null
? new MainView()
: new LoadingScreen();
} else {
return new ErrorScreen(error: snapshot.error);
}
}
},
);
Above code work fine but for beginners I make it little bit simple
main.dart
import 'package:flutter/material.dart';
import 'package:healthtic/IntroScreen.dart';
import 'package:healthtic/user_preferences.dart';
import 'login.dart';
import 'profile.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
// This widget is the root of your application.
bool isLoggedIn = false;
MyAppState() {
MySharedPreferences.instance
.getBooleanValue("isfirstRun")
.then((value) => setState(() {
isLoggedIn = value;
}));
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
//if true return intro screen for first time Else go to login Screen
home: isLoggedIn ? Login() : IntroScreen());
}
}
then share preferences
MySharedPreferences
import 'package:shared_preferences/shared_preferences.dart';
class MySharedPreferences {
MySharedPreferences._privateConstructor();
static final MySharedPreferences instance =
MySharedPreferences._privateConstructor();
setBooleanValue(String key, bool value) async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
myPrefs.setBool(key, value);
}
Future<bool> getBooleanValue(String key) async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
return myPrefs.getBool(key) ?? false;
}
}
then create two dart files IntroScreen and Login
Intro Screen will apear just once when user run application for first time usless the app is removed or caches are cleard
IntroScreen
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:healthtic/SliderModel.dart';
import 'package:healthtic/login.dart';
import 'package:healthtic/user_preferences.dart';
import 'package:shared_preferences/shared_preferences.dart';
class IntroScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Healthtic",
home: IntorHome(),
debugShowCheckedModeBanner: false,
);
}
}
class IntorHome extends StatefulWidget {
#override
_IntorHomeState createState() => _IntorHomeState();
}
class _IntorHomeState extends State<IntorHome> {
List<SliderModel> slides=new List<SliderModel>();
int currentIndex=0;
PageController pageController=new PageController(initialPage: 0);
#override
void initState() {
// TODO: implement initState
super.initState();
slides=getSlides();
}
Widget pageIndexIndicator(bool isCurrentPage) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 2.0),
height: isCurrentPage ? 10.0 : 6.0,
width: isCurrentPage ? 10.0 :6.0,
decoration: BoxDecoration(
color: isCurrentPage ? Colors.grey : Colors.grey[300],
borderRadius: BorderRadius.circular(12)
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView.builder(
controller: pageController,
onPageChanged: (val){
setState(() {
currentIndex=val;
});
},
itemCount: slides.length,
itemBuilder: (context,index){
return SliderTile(
ImageAssetPath: slides[index].getImageAssetPath(),
title: slides[index].getTile(),
desc: slides[index].getDesc(),
);
}),
bottomSheet: currentIndex != slides.length-1 ? Container(
height: Platform.isIOS ? 70:60,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
onTap: (){
pageController.animateToPage(slides.length-1, duration: Duration(
microseconds: 400,
), curve: Curves.linear);
},
child: Text("Skip")
),
Row(
children: <Widget>[
for(int i=0;i<slides.length;i++) currentIndex == i ?pageIndexIndicator(true): pageIndexIndicator(false)
],
),
GestureDetector(
onTap: (){
pageController.animateToPage(currentIndex+1, duration: Duration(
microseconds: 400
), curve: Curves.linear);
},
child: Text("Next")
),
],
),
) : Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
height: Platform.isIOS ? 70:60,
color: Colors.blue,
child:
RaisedButton(
child: Text("Get Started Now",style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w300
),
),
onPressed: (){
MySharedPreferences.instance
.setBooleanValue("isfirstRun", true);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => Login()),
);
},
),
),
);
}
}
class SliderTile extends StatelessWidget {
String ImageAssetPath, title, desc;
SliderTile({this.ImageAssetPath, this.title, this.desc});
#override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(ImageAssetPath),
SizedBox(height: 20,),
Text(title),
SizedBox(height: 12,),
Text(desc),
],
)
,
);
}
}
final step Login
import 'package:flutter/material.dart';
import 'package:healthtic/user_preferences.dart';
import 'profile.dart';
class Login extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return LoginState();
}
}
class LoginState extends State<Login> {
TextEditingController controllerEmail = new TextEditingController();
TextEditingController controllerUserName = new TextEditingController();
TextEditingController controllerPassword = new TextEditingController();
#override
Widget build(BuildContext context) {
final formKey = GlobalKey<FormState>();
// TODO: implement build
return SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.all(25),
child: Form(
key: formKey,
autovalidate: false,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Email Id:", style: TextStyle(fontSize: 18)),
SizedBox(width: 20),
Expanded(
child: TextFormField(
controller: controllerEmail,
decoration: InputDecoration(
hintText: "Please enter email",
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value.trim().isEmpty) {
return "Email Id is Required";
}
},
),
)
],
),
SizedBox(height: 60),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("UserName:", style: TextStyle(fontSize: 18)),
SizedBox(width: 20),
Expanded(
child: TextFormField(
decoration: InputDecoration(
hintText: "Please enter username",
),
validator: (value) {
if (value.trim().isEmpty) {
return "UserName is Required";
}
},
controller: controllerUserName),
)
],
),
SizedBox(height: 60),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Password:", style: TextStyle(fontSize: 18)),
SizedBox(width: 20),
Expanded(
child: TextFormField(
decoration: InputDecoration(
hintText: "Please enter password",
),
obscureText: true,
validator: (value) {
if (value.trim().isEmpty) {
return "Password is Required";
}
},
controller: controllerPassword),
)
],
),
SizedBox(height: 100),
SizedBox(
width: 150,
height: 50,
child: RaisedButton(
color: Colors.grey,
child: Text("Submit",
style: TextStyle(color: Colors.white, fontSize: 18)),
onPressed: () {
if(formKey.currentState.validate()) {
var getEmail = controllerEmail.text;
var getUserName = controllerUserName.text;
var getPassword = controllerPassword.text;
MySharedPreferences.instance
.setBooleanValue("loggedin", true);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => Profile()),
);
}
},
),
)
],
),
),
),
)),
);
}
}
Can be possible with this pakage : https://pub.dev/packages/is_first_run
Usage :
bool firstRun = await IsFirstRun.isFirstRun();

Resources