How to pass variable and get data from API in flutter? - flutter

This the first UI in there when user enter channel name and after click join then should it pass "loadData" method in there that channel name should pass to "API" and get channelname and appId from that url.
join button code
Future<void> onJoin() async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => loadData(myController.text)),
);
}
loadData method
import 'dart:convert';
import 'package:http/http.dart';
import '../model/appIdModel.dart';
class ApiService {
loadData(String myController) async {
final String url ='https://jsonplaceholder.typicode.com/posts/1=$myController';
Future<List<Data>> getData() async {
Response response = await get(Uri.parse(url));
if (response.statusCode == 2000) {
Map<String, dynamic> json = jsonDecode(response.body);
List<dynamic> body = json['data'];
List<Data> datas = body.map((dynamic item) => Data.fromJson(item).toList();
return datas;
} else {
throw ('cannot fetch data');
}
}
}
}
Data model code
class Data {
String appId;
String channelName;
Data({
required this.appId,
required this.channelName,
});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
appId: json['appId'] == null ? null : json['appId'],
channelName: json['channelName'] == null ? null : json['channelName']);
}
}
then appId and channelName should fetch
FutureBuilder widget code show channelId and channelName (Home page code)
class _MyHomePageState extends State<MyHomePage> {
final myController = TextEditingController();
bool _validateError = false;
ApiService client = ApiService();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
clipBehavior: Clip.antiAliasWithSaveLayer,
physics: BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 20)),
Padding(padding: EdgeInsets.symmetric(vertical: 20)),
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: TextFormField(
controller: myController,
decoration: InputDecoration(
labelText: 'Channel Name',
labelStyle: TextStyle(color: Colors.blue),
hintText: 'test',
hintStyle: TextStyle(color: Colors.black45),
errorText:
_validateError ? 'Channel name is mandatory' : null,
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(20),
),
),
),
),
Padding(padding: EdgeInsets.symmetric(vertical: 30)),
Container(
width: MediaQuery.of(context).size.width * 0.25,
child: MaterialButton(
onPressed: onJoin,
height: 40,
color: Colors.blueAccent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
'Join',
style: TextStyle(color: Colors.white),
),
Icon(
Icons.arrow_forward,
color: Colors.white,
),
],
),
),
),
Center(
child: FutureBuilder(
future: client.getData(),
builder: (BuildContext context,
AsyncSnapshot<List<Data>> snapshot) {
if (snapshot.hasData) {
List<Data>? data = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) => Column(
children: [
Text(
data![index].channelName.toString(),
),
Text(
data[index].appId.toString(),
),
],
));
}
return const Center(
child: CircularProgressIndicator(),
);
}),
)
],
),
),
),
),
);
}
Future<void> onJoin() async {
setState(() {
myController.text.isEmpty
? _validateError = true
: _validateError = false;
});
// await _handleCameraAndMic(Permission.camera);
// await _handleCameraAndMic(Permission.microphone);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => loadData(myController.text)),
);
}
this is the my code to fetch data.when I run the url with channel name then data show nicely.
I tired to fetch "channelName" and "appId" using this url.

You are doing wrong when you call onJoin, change it to this:
Future<void> onJoin() async {
future = client.getData(myController.text);
}
then define new variable like this:
Future<List<Data>>? future;
then change ApiService to this:
class ApiService {
final String url =
'https://jsonplaceholder.typicode.com/posts/1';
Future<List<Data>> getData(String myController) async {
Response response = await get(Uri.parse(url + myController));
if (response.statusCode == 200) { // <--- change this
Map<String, dynamic> json = jsonDecode(response.body);
List<dynamic> body = json['data'];
List<Data> datas = body.map((dynamic item) => Data.fromJson(item).toList();
return datas;
} else {
throw ('cannot fetch data');
}
}
}
then change your FutureBuilder to this
future != null ? FutureBuilder(//<--- add this
future: future,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
future = null; //<--- add this
List<Data> data = snapshot.data ?? []; //<-- change this
return ListView.builder(
itemCount: data.length, //<-- add this
itemBuilder: (context, index) => Column(
children: [
Text(
data[index].channelName.toString(),
),
Text(
data[index].appId.toString(),
),
],
));
}
}
},
)
: SizedBox(),//<--- add this
also as you can see in your postman result the data contain a map, not list of map, so if you expect a list you need to contact to your backend but if not you can parse it like this:
Map<String, dynamic> body = json['data'];
List<Data> datas = [Data.fromJson(body)];
also for ui issue you can't use listview inside SingleChildScrollView, for that you need set shrinkWrap to true, also set its physics to NeverScrollableScrollPhysics too:
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: ...
)

Related

Error: NoSuchMethodError: 'then' Dynamic call of null. Receiver: null

Does anyone know the cause of this error? I have tried many ways but still don't know where the problem is.
Database.dart
import 'package:fitness_app/Login/login_data.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseService {
static final DatabaseService _databaseService = DatabaseService._internal();
factory DatabaseService() => _databaseService;
DatabaseService._internal();
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'conference.database');
return await openDatabase(
path,
onCreate: _onCreate,
version: 1,
onConfigure: (db) async => await db.execute('PRAGMA foreign_keys = ON'),
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE login(id INTEGER PRIMARY KEY, name TEXT, username TEXT, password TEXT)',
);
}
verifyuser(String user, String pass) {}
insertLoginData(Logindata logindata) {}
}
Login.dart
import 'package:fitness_app/Login/signup.dart';
import 'package:flutter/material.dart';
import 'login_data.dart';
import 'package:fitness_app/Database/database.dart';
import 'package:fitness_app/home_page.dart';
class Login extends StatefulWidget {
const Login({Key? key, this.login}) : super(key: key);
final Logindata? login;
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
String email = "a";
String pass = "a";
TextEditingController emails = TextEditingController();
TextEditingController password = TextEditingController();
final _formKey = GlobalKey<FormState>();
static final List<Logindata> _login = [];
final DatabaseService _databaseService = DatabaseService();
Future<List<Logindata>> _getLogin() async {
await _databaseService.verifyuser(email, pass).then((value) {
if (value) {
AlertDialog alert = AlertDialog(
title: const Text('Login successful!'),
content: const Text('Welcome!'),
actions: <Widget>[
TextButton(
onPressed: () => (Navigator.push(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
)),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
} else {
AlertDialog alert = AlertDialog(
title: const Text('Error!'),
content: const Text('Wrong Email or Password'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
});
return _login;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
Container(
padding: EdgeInsets.all(10),
child: Form(
key: _formKey,
child: Column(
children: [
Text(
'BeFit:Fitness Activity Tracker Progress\n\n',
style: TextStyle(fontSize: 24),
textAlign: TextAlign.start,
),
Text(
'Welcome',
style: TextStyle(fontSize: 40),
textAlign: TextAlign.center,
),
SizedBox(
height: 30,
),
Container(
child: TextFormField(
controller: emails,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
child: TextFormField(
controller: password,
obscureText: true,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide:
BorderSide(width: 1, color: Colors.grey)),
labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
return null;
},
),
),
SizedBox(
height: 20,
),
Container(
width: MediaQuery.of(context).size.width,
height: 50,
child: FlatButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
email = emails.text;
pass = password.text;
_getLogin();
print(email);
print(pass);
print('success');
}
},
child: Text("Login"),
textColor: Colors.white,
color: Colors.deepPurple[400],
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(10)),
),
),
SizedBox(
height: 20,
),
Container(
child: Row(
children: <Widget>[
Text('Does not have account?'),
FlatButton(
textColor: Colors.deepPurpleAccent[100],
child: Text(
'Sign up',
style: TextStyle(fontSize: 16),
),
onPressed: () {
//signup screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SignUp()),
);
},
)
],
mainAxisAlignment: MainAxisAlignment.center,
))
],
),
),
)
],
),
),
),
);
}
}
Here is the error
enter image description here
I'm using this flutter to complete my project but there are some errors that I can't solve it. Sorry if my coding looks not right because still lacks in coding
Your verifyuser function does not return anything so your value returns null. You either need to return something or check for null values in your then statement.
Future<String> verifyuser(String user, String pass)async {
return user + pass;
}

flutter riverpod leaving screen then come back it doesn't maintain the state

So I have two screens:
-Book_screen to display all the books(click on any book to go to article_screen)
-article_screen to display articles
In article_screen, I can click on article to save it as favorites.
but when I go back to book_screen then come back to article_screen, those favorited articles doesn't show the favorited status(icon red heart).
this is my article screen code:
class ArticleENPage extends ConsumerStatefulWidget{
final String bookName;
const ArticleENPage({Key? key,#PathParam() required this.bookName,}) : super(key: key);
#override
ArticleENScreen createState()=> ArticleENScreen();
}
class ArticleENScreen extends ConsumerState<ArticleENPage> {
late Future<List<Code>> codes;
#override
void initState() {
super.initState();
codes = fetchCodes();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.bookName,style: const TextStyle(fontSize: 24,fontWeight: FontWeight.bold),),backgroundColor: Colors.white,foregroundColor: Colors.black,elevation: 0,),
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Container(
margin: const EdgeInsets.only(top:10),
height: 43,
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
border: Border.all(
color: Colors.black.withOpacity(0.32),
),
),
child: Consumer(
builder: (context,ref,_) {
return TextField(
onChanged: (value) {
searchStringController controller = ref.read(searchStringProvider.notifier);
controller.setText(value.toLowerCase());
},
decoration: const InputDecoration(
border: InputBorder.none,
icon: Icon(Icons.search,size:18),
hintText: "Search Here",
hintStyle: TextStyle(color: Color.fromRGBO(128,128, 128, 1)),
),
);
}
),
),
),
const SizedBox(height: 10),
Expanded(
child: FutureBuilder(
builder: (context, AsyncSnapshot<List<Code>> snapshot) {
if (snapshot.hasData) {
return Center(
child: Consumer(
builder: (context,ref,child) {
final searchString = ref.watch(searchStringProvider);
return ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return snapshot.data![index].name
.toLowerCase()
.contains(searchString) ||
snapshot.data![index].description
.toLowerCase()
.contains(searchString)
? Consumer(
builder: (context,ref,child) {
final favlist = ref.watch(FavoriteListController.favoriteListProvider);
print(favlist);
final alreadySaved = favlist.contains(snapshot.data![index]);
return Card(
child:Padding(
padding: const EdgeInsets.all(10),
child:ExpandableNotifier(
child: ScrollOnExpand(
child: ExpandablePanel(
theme: const ExpandableThemeData(hasIcon: true),
header: RichText(text: TextSpan(children: highlight(snapshot.data![index].name, searchString,'title')),),
collapsed: RichText(text: TextSpan(children: highlight(snapshot.data![index].description, searchString,'content')), softWrap: true, maxLines: 3, overflow: TextOverflow.ellipsis,),
expanded: Column(
children: [
RichText(text: TextSpan(children: highlight(snapshot.data![index].description, searchString,'content')), softWrap: true ),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
semanticLabel: alreadySaved ? 'Remove from saved' : 'Save',
),
onPressed: () {
FavoriteListController controller = ref.read(FavoriteListController.favoriteListProvider.notifier);
if (alreadySaved) {
controller.toggle(snapshot.data![index]);
} else {
controller.toggle(snapshot.data![index]);
}
},
),
IconButton(
icon: const Icon(Icons.content_copy),
onPressed: () {
setState(() {
Clipboard.setData(ClipboardData(text: snapshot.data![index].name+"\n"+snapshot.data![index].description))
.then((value) {
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('Copied')));
},);
});
},
),],),],)),),)));})
: Container();
},
separatorBuilder: (BuildContext context, int index) {
return snapshot.data![index].name
.toLowerCase()
.contains(searchString) ||
snapshot.data![index].description
.toLowerCase()
.contains(searchString)
? Divider()
: Container();
},
);
}
),
);
} else if (snapshot.hasError) {
return const Center(child: Text('Something went wrong :('));
}
return const Align(alignment:Alignment.topCenter,child:CircularProgressIndicator());
},
future: codes,
),
),
],
),
);
}
//read from files
Future<List<Code>> fetchCodes() async {
final response =
await rootBundle.loadString('assets/articles.json');
var CodeJson = json.decode(response)[widget.bookName] as List<dynamic>;
return CodeJson.map((code) => Code.fromJson(code)).toList();
}
}
I tried using riverpod for provider and save to sharedpreference the list of code that I favorited.
final sharedPrefs =
FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
class FavoriteListController extends StateNotifier<List<Code>>{
FavoriteListController(this.pref) : super(Code.decode(pref?.getString("favcode")??""));
static final favoriteListProvider = StateNotifierProvider<FavoriteListController, List<Code>>((ref) {
final pref = ref.watch(sharedPrefs).maybeWhen(
data: (value) => value,
orElse: () => null,
);
print(pref?.getString("favcode"));
return FavoriteListController(pref);
});
final SharedPreferences? pref;
void toggle(Code code) {
if (state.contains(code)) {
state = state.where((id) => id != code).toList();
} else {
state = [...state, code];
}
final String encodedData = Code.encode(state);
pref!.setString("favcode", encodedData);
}
}
I am not sure what is the cause of this but I think it might be because of futurebuilder? I am confused to how to solve this issue...
I am stuck in a dead end so any help or advice would be really appreciated
edit 1-
this is my source code in case I have not include all the necessary codes
https://github.com/sopheareachte/LawCode
edit-2
do I need to change "late Future<List> codes;" that fetch all the codes for futurebuilder to riverpod futureprovider too for it to work?
Maybe the problem is, that you define a static provider inside of your controller class. Try this code:
final sharedPrefs = FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
final favoriteListProvider = StateNotifierProvider<FavoriteListController, List<Code>>((ref) {
final pref = ref.watch(sharedPrefs).maybeWhen(
data: (value) => value,
orElse: () => null,
);
print(pref?.getString("favcode"));
return FavoriteListController(pref);
});
class FavoriteListController extends StateNotifier<List<Code>>{
FavoriteListController(this.pref) : super(Code.decode(pref?.getString("favcode")??""));
final SharedPreferences? pref;
void toggle(Code code) {
if (state.contains(code)) {
state = state.where((id) => id != code).toList();
} else {
state = [...state, code];
}
final String encodedData = Code.encode(state);
pref!.setString("favcode", encodedData);
}
}

flutter : How to use shared preferences to keep user logged in

I want to keep the user logged in after the user successfully logsin in flutter. I am using a REST API to retrieve the user name and password of the user. But I want to save those details so that the user can stay logged in. My current situation is i can successfully log the user in but when i restart the app i have to login again so i need to save the details of the user in a shared preference so that the user can stay logged for the entire session until logout.But i am unable to do that so please help me with it. Thanks in advance
Login.dart
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(3, 9, 23, 1),
body: Container(
padding: EdgeInsets.only(
top: 100,
right: 30,
left: 30,
),
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FadeAnimation(
1.2,
Container(
padding: EdgeInsets.all(70.0),
margin: EdgeInsets.only(bottom: 50.0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/trakinglogo.png'))),
)),
SizedBox(
height: 30,
),
FadeAnimation(
1.5,
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey[300]))),
child: TextFormField(
controller: _emailController,
onFieldSubmitted: (_) =>
FocusScope.of(context).nextFocus(),
textInputAction: TextInputAction.done,
validator: (value) {
if (value.isEmpty) {
return 'Email is required';
}
return null;
},
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Colors.grey.withOpacity(.8)),
hintText: "Votre adresse mail"),
),
),
Container(
decoration: BoxDecoration(),
child: TextFormField(
controller: _passwordController,
validator: (value) {
if (value.isEmpty) {
return 'Password is required';
}
return null;
},
obscureText: _isHidden,
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Colors.grey.withOpacity(.8)),
hintText: "Mot de passe",
suffix: InkWell(
onTap: _togglePasswordView,
child: Icon(
_isHidden
? (CommunityMaterialIcons
.eye_outline)
: (CommunityMaterialIcons.eye_off),
color: Color(0xFF939394),
SizedBox(
height: 40,
),
FadeAnimation(
1.8,
Center(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: RaisedButton(
textColor: Colors.white,
color: kPrimaryColor,
child: Text("Se connecter"),
onPressed: () async {
if (_formKey.currentState.validate()) {
showDialog(
context: context,
builder: (BuildContext context) {
return Center(
child:
CircularProgressIndicator(),
);
});
await loginUser();
}
/* Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyWidget()),
);*/
// Navigator.pushNamed(context, 'Mywidget');
},
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(30.0),
),
),
width: 250,
padding: EdgeInsets.all(15),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AlreadyHaveAnAccountCheck(
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return SignUp();
void loginUser() async {
// if (_formKey.currentState.validate()) {
setState(() {
_isLoading = true;
});
String email = _emailController.text;
String password = _passwordController.text;
authentication.login(email, password).then((user) {
if (user != null)
Navigator.push(
context, MaterialPageRoute(builder: (context) => MyWidget()));
}).catchError((error) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error.toString())));
});
setState(() {
_isLoading = false;
});
}
and this is login function :
Future<User> login(String email, String password) async {
await checkInternet();
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
Map<String, String> body = {'email': email, 'password': password};
var response = await http.post(Uri.parse(ApiUtil.AUTH_LOGIN),
headers: headers, body: jsonEncode(body));
switch (response.statusCode) {
case 200:
var body = jsonDecode(response.body);
var data = body['user'];
User user = User.fromJson(data);
Track track = Track.fromJson(body);
if (body['code'] == 0) {
SharedPreferences localStorage =
await SharedPreferences.getInstance();
localStorage.setInt('id', body['user']['id']);
localStorage.setString('adress', body['user']['adress']);
localStorage.setString('phone', body['user']['phone']);
localStorage.setString('access_token', body['access_token']);
localStorage.setString('user', json.encode(body['user']));
String user = localStorage.getString('user');
}
return user;
case 500:
throw ('Erreur serveur');
break;
case 400:
throw LoginFailed();
default:
throw ('connection timeout');
break;
}
}
Make a class AppSharedPreference and in that class make these methods:
User is your modal class.
Save User:
static saveUser(String key, User value) async {
final prefs = await SharedPreferences.getInstance();
//We cannot save custom objects.
//So json.encode is used to convert User Object to Json a string.
prefs.setString(key, json.encode(value));
}
Get User:
static readUser(String key) async {
final prefs = await SharedPreferences.getInstance();
//json.decode to convert json string back to Json Object
return json.decode(prefs.getString(key));
}
Make Sure your modal User class contains toJson method:
Map<String, String> toJson() => {
'id': Id,
'address': address,
'phone': phone,
// rest of your attributes...
};
To call the saveUser method:
await AppSharedPreference.saveUser('your_key', user);
To call the readUser method:
(make sure your modal User class contains fromJson method)
User user = User.fromJson(await AppSharedPreference.readUser('your_key'));
And after that you can check whether the user is null or not. If it is null it means there is no data and the user is not logged-in.
In case if you want to logout user you can create a method to remove the user from SharedPreference:
Remove User:
static removeUser(String key) async {
final prefs = await SharedPreferences.getInstance();
prefs.remove(key);
}
To call removeUser method:
await AppSharedPreference.removeUser('your_key');
You have to check if there is data in the sharedPreference at the starting of the app and then redirect users based on that. An example would be implementing this in splashscreen
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'home_page.dart';
import 'login_page.dart';
class SplashScreen extends StatefulWidget {
static const String splashRoute = '/';
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
void timeToShowSplashScreenOnScreen() async {
Timer(Duration(seconds: 3), () async {
if (!mounted) return;
SharedPreferences _prefs = await SharedPreferences.getInstance();
bool isLoggedIn = _prefs.getBool("loggedIn");
if (isLoggedIn != null && isLoggedIn) {
Navigator.of(context).pushNamedAndRemoveUntil(HomePage.homePageRoute, (route) => false);
} else {
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.loginRoute, (route) => false);
}
});
}
#override
void initState() {
super.initState();
timeToShowSplashScreenOnScreen();
}
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
double _height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
width: _width,
height: _height,
child: Stack(
children: [
// Your UI for splash screen
],
),
),
);
}
}
This example uses boolean values stored in prefs but you can do similar things with strings too.

implement onTap in flutter

I try to create an onTap function on ChatRoomListTile which navigates me to the chatscreen where the id is = chatRoomId. Unfortunately I don't know how to deliver the ID.
Unfortunately I am very unexperienced in flutter and I made this app via tutorial... It works nice but I am still learning, so a big big big thanks!
my Database.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:signal/helperfunctions/sharedpref_helper.dart';
class DatabaseMethods{
Future addUserInfoToDB(String userId, Map<String, dynamic>userInfoMap)
async {
return FirebaseFirestore.instance
.collection("users")
.doc(userId)
.set(userInfoMap);
}
Future<Stream<QuerySnapshot>> getUserByUserName(String username) async{
return FirebaseFirestore.instance
.collection("users")
.where("username", isEqualTo: username)
.snapshots();
}
Future addMessage(String chatRoomId, String messageId, Map messageInfoMap) async {
return FirebaseFirestore.instance
.collection ("chatrooms")
.doc(chatRoomId)
.collection("chats")
.doc(messageId)
.set(messageInfoMap);
}
updateLastMessageSend(String chatRoomId, Map lastMessageInfoMap){
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.update(lastMessageInfoMap);
}
createChatRoom(String chatRoomId, Map chatRoomInfoMap) async{
final snapShot = await FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.get();
if(snapShot.exists){
//chatroom already exists
return true;
}else{
//chatroom does not exists
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.set(chatRoomInfoMap);
}
}
Future<Stream<QuerySnapshot>> getChatRoomMessages(chatRoomId) async {
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.collection("chats")
.orderBy("ts", descending: true)
.snapshots();
}
// Future<Stream<QuerySnapshot>> openChatRoom(String chatRoomId) async{
// return FirebaseFirestore.instance
// .collection("chatrooms")
// .doc(chatRoomId)
// .collection("chats")
// .snapshots();
// }
Future<Stream<QuerySnapshot>> getChatRooms() async {
String myUsername = await SharedPreferenceHelper().getUserName();
return FirebaseFirestore.instance
.collection("chatrooms")
.orderBy("lastMessageSendTs", descending: true)
.where("users",arrayContains: myUsername)
.snapshots();
}
Future<QuerySnapshot> getUserInfo(String username) async {
return await FirebaseFirestore.instance
.collection("users")
.where("username", isEqualTo: username)
.get();
}
}
and my home.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:signal/helperfunctions/sharedpref_helper.dart';
import 'package:signal/services/auth.dart';
import 'package:signal/services/database.dart';
import 'package:signal/views/signin.dart';
import 'services/colorpicker.dart';
import 'chatscreen.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isSearching = false;
String myName, myProfilePic, myUserName, myEmail, messageSentTs;
Stream usersStream, chatRoomsStream;
TextEditingController searchUsernameEditingController =
TextEditingController();
getMyInfoFromSharedPreference() async{
myName = await SharedPreferenceHelper().getDisplayName();
myProfilePic = await SharedPreferenceHelper().getUserProfileUrl();
myUserName = await SharedPreferenceHelper().getUserName();
myEmail = await SharedPreferenceHelper().getUserEmail();
}
//dieser string sollte eigentlich aus sicherheitsgründen random generiert werden
getChatRoomIdByUsernames(String a, String b){
if(a.substring(0,1).codeUnitAt(0) > b.substring(0,1).codeUnitAt(0)){
return "$b\_$a";
}else{
return "$a\_$b";
}
}
onSearchBtnClick() async {
isSearching = true;
setState(() {});
usersStream = await DatabaseMethods()
.getUserByUserName(searchUsernameEditingController.text);
setState(() {});
}
Widget chatRoomsList(){
return StreamBuilder(
stream: chatRoomsStream,
builder: (context, snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, index){
DocumentSnapshot ds = snapshot.data.docs[index];
return ChatRoomListTile(ds["lastMessage"], ds.id,myUserName);
})
: Center(child: CircularProgressIndicator());
},
);
}
// print("this is the values we have $myUserName $username"));
// Chatroom ID Kontrolle ( achtung beim randomisen der CRId)
Widget searchUserListTile({String profileUrl, name, username, email}){
return GestureDetector(
onTap: (){
var chatRoomId = getChatRoomIdByUsernames(myUserName, username);
Map<String, dynamic> chatRoomInfoMap = {
"users" : [myUserName, username]
};
DatabaseMethods().createChatRoom(chatRoomId, chatRoomInfoMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(username, name)
)
);
},
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(30),
child: Image.network(
profileUrl,
height: 20,
width: 20,
),
),
SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Text(name),
Text(email),
],
)
],
),
);
}
Widget searchUsersList(){
return StreamBuilder(
stream: usersStream,
builder: (context, snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, index){
DocumentSnapshot ds = snapshot.data.docs[index];
return searchUserListTile(
profileUrl: ds["imgUrl"],
name: ds["name"],
username: ds["username"],
email: ds["email"]
);
},
) : Center(child: CircularProgressIndicator(),);
}
);
}
getChatRooms() async {
chatRoomsStream = await DatabaseMethods().getChatRooms();
setState(() {});
}
onScreenLoaded() async {
await getMyInfoFromSharedPreference();
getChatRooms();
}
#override
void initState() {
onScreenLoaded();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(233, 240, 244, 1),
appBar: AppBar(
title: Image.asset('assets/images/logo.png', width: 40,),
elevation: 0.0,
backgroundColor: Colors.white,
actions: [
InkWell(
onTap: (){
AuthMethods().signOut().then((s) {
Navigator.pushReplacement(context, MaterialPageRoute
(builder: (context) => SignIn()));
});
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.exit_to_app)),
)
],
),
body: Container(
margin: EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Row(
children: [
isSearching
? GestureDetector(
onTap: (){
isSearching = false;
searchUsernameEditingController.text = "";
setState(() {});
},
child: Padding(
padding: EdgeInsets.only(right: 12),
child: Icon(Icons.arrow_back)),
) : Container(),
Expanded(
child: Container(
// Disable Searchbar
width: 0,
height: 0,
margin: EdgeInsets.symmetric(vertical: 16),
padding: EdgeInsets.symmetric(horizontal:16),
decoration: BoxDecoration
(border: Border.all(
color: Colors.black87,
width: 1,
style: BorderStyle.solid), borderRadius: BorderRadius.circular(24)),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchUsernameEditingController,
decoration: InputDecoration(
border: InputBorder.none, hintText: "Finde einen Freund"),
)),
GestureDetector(
onTap:(){
if(searchUsernameEditingController.text != ""){
onSearchBtnClick();
}
},
// Disable Search Icon
// child: Icon(Icons.search),
)
],
),
),
)
]
),
isSearching ? searchUsersList() : chatRoomsList()
],
),
),
);
}
}
class ChatRoomListTile extends StatefulWidget {
final String lastMessage, chatRoomId, myUsername;
ChatRoomListTile(this.lastMessage, this.chatRoomId, this.myUsername);
#override
_ChatRoomListTileState createState() => _ChatRoomListTileState();
}
class _ChatRoomListTileState extends State<ChatRoomListTile> {
String profilePicUrl ="", name="", username="";
getThisUserInfo() async {
username = widget.chatRoomId.replaceAll(widget.myUsername, "").replaceAll("_", "");
QuerySnapshot querySnapshot = await DatabaseMethods().getUserInfo(username);
name = "${querySnapshot.docs[0]["name"]}";
profilePicUrl = "${querySnapshot.docs[0]["imgUrl"]}";
setState(() {});
}
#override
void initState() {
getThisUserInfo();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 2, horizontal: 0,),
padding: EdgeInsets.symmetric(horizontal:12,vertical: 20,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
profilePicUrl,
height: 35,
width: 35,
),
),
SizedBox(width: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto',
color: Color.fromRGBO(57, 59, 85, 0.8),
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 5),
Container(
width: (MediaQuery.of(context).size.width) - 150,
child: Text(
widget.lastMessage,
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: Color.fromRGBO(171, 183, 201, 1),
fontWeight: FontWeight.w400,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
)
],
),
);
}
}
you can pass the id as a constructor from the listTile,
Navigator.of(context).push.... ChatScreen(id: chatRoomId)
as so, and in ChatScreen you can receive it like so
class ChatScreenextends StatefulWidget {
final String id;
ChatScreen({requried this.id});
#override
_ChatScreenState createState() => _ChatScreenState ();
}
class _ChatScreenState extends State<ChatScreen> {
if it is a stateless Widget class you can directly access the id as id, if it is a stateful Widget then widget.id ,
Another way of doing this is passing as Navigator argument you can check the docs for more info Flutter Docs
Hello and thank you very much for your time ... i tried to implement it but unfortunate i get an error. here the GestureDetector i added to my home.dart ::: I also tried to change id: chatRoomId to this.Id or just chatRoomId .. but it keeps underlined and erroring :
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ChatScreen(id: chatRoomId)));
},
child: Container(
margin: EdgeInsets.symmetric(vertical: 2, horizontal: 0,),
padding: EdgeInsets.symmetric(horizontal:12,vertical: 20,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
and here the update of the chatscreen.dart
class ChatScreen extends StatefulWidget {
final String chatWidthUsername, name, id;
ChatScreen(this.chatWidthUsername, this.name, {this.id});
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
String chatRoomId, messageId = "";
Stream messageStream;
String myName, myProfilePic, myUserName, myEmail;
TextEditingController messageTextEditingController = TextEditingController();
thanks again so much !
greetings
alex

how to get data in material datatable in flutter from rest api

I want to show data in material dataTable but instead of getting data in a row it makes the table head every time with the row
My design which I Want:
The design I am getting right now:
Here is the full code of my flutter application:
UI SCREEN
________________This is Ui part of my application_
import 'package:aiims/bloc/add_relatives_bloc.dart';
import 'package:aiims/models/relative.dart';
import 'package:aiims/service/api.dart';
import 'package:aiims/widgets/side_menu_widget.dart';
import 'package:flutter/material.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
class RelativeScreen extends StatefulWidget {
#override
_RelativeScreenState createState() => _RelativeScreenState();
}
class _RelativeScreenState extends State<RelativeScreen> {
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
DataRow getDataRow(data) {
return DataRow(
cells: <DataCell>[
DataCell(Text(data["name"])),
DataCell(Text(data["age"])),
DataCell(Text(data["relation"])),
DataCell(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.edit_outlined),
Icon(Icons.delete_outline_sharp),
],
),
),
],
);
}
#override
void initState() {
// TODO: implement initState
super.initState();
// _getDataRow(list.length);
}
// _getRelatives() {
// getRelativesList().then((Relative) {
// setState(() {
//
// });
// print("Length: ${_relative.length}");
// });
// }
// DataRow _getDataRow(list) {
// return DataRow(
// cells: <DataCell>[
// DataCell(Text(list["name"])),
// DataCell(Text(list["age"])),
// DataCell(Text(list["relation"])),
// ],
// );
// }
#override
Widget build(BuildContext context) {
final bloc = Provider.of<AddNewRelativeBloc>(context, listen: false);
return Scaffold(
drawer: NavDrawer(),
appBar: AppBar(
title: Text('Relatives'),
actions: <Widget>[
GestureDetector(
onTap: () => _add_relavitves(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Icon(Icons.add_circle_outline_rounded),
),
),
],
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
// alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 20),
child: SingleChildScrollView(
child: Container(
child: FutureBuilder(
future: getRelativesList(),
// initialData: new TreatmentDetail(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else if (snapshot.connectionState ==
ConnectionState.done) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
return DataTable(
headingRowColor: MaterialStateColor
.resolveWith(
(states) => Color(0xffff69b4)),
// MaterialStateColor.resolveWith((states) => Colors.pink),
columns: [
DataColumn(label: Text("Name")),
DataColumn(label: Text("Age")),
DataColumn(label: Text("Relation")),
DataColumn(label: Text("Action")),
],
// rows: List.generate(
// list.length, (index) =>
// _getDataRow(list[index]),
// ));
rows: [
DataRow(
cells: [
DataCell(Text(list[index]["name"])),
DataCell(Text(list[index]["age"])),
DataCell(Text(list[index]["relation"])),
DataCell(
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.edit_outlined),
Icon(Icons.delete_outline_sharp),
],
),
),
],
),
// DataRow(cells: [
// DataCell(Text('Ajay Singh')),
// DataCell(Text('25')),
// DataCell(Text('Son')),
// DataCell(
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Icon(Icons.edit_outlined),
// Icon(Icons.delete_outline_sharp),
// ],
// ),
// ),
// ]),
],
);
});
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
),
),
),
),
),
);
}
void _add_relavitves() {
final bloc = Provider.of<AddNewRelativeBloc>(context, listen: false);
// set up the buttons
// Widget cancelButton = FlatButton(
// child: Text("Cancel"),
// onPressed: () {
// Navigator.pop(context);
// },
// );
Widget cancelButton = MaterialButton(
child: Container(
height: 40,
width: 110,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Color(0xffff69b4),
),
child: Text(
"Discard",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
onPressed: () {
Navigator.pop(context);
},
);
Widget continueButton = FlatButton(
child: Text("Continue"),
onPressed: () {},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Center(
child: Text(
"Add New Relative",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
content: SingleChildScrollView(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 20),
StreamBuilder<Object>(
stream: bloc.name,
builder: (context, snapshot) {
return TextField(
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: "Name",
labelText: "Name",
errorText: snapshot.error,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// onChanged: (value) => bloc.changeName,
onChanged: bloc.changeName,
);
}),
SizedBox(height: 20),
StreamBuilder<Object>(
stream: bloc.age,
builder: (context, snapshot) {
return TextField(
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: "Age",
labelText: "Age",
errorText: snapshot.error,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// onChanged: (value) => bloc.changeName,
onChanged: bloc.changeAge,
);
}),
SizedBox(height: 20),
StreamBuilder<Object>(
stream: bloc.relation,
builder: (context, snapshot) {
return TextField(
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: "Relation",
labelText: "Relation",
errorText: snapshot.error,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// onChanged: (value) => bloc.changeName,
onChanged: bloc.changeRelation,
);
}),
SizedBox(height: 20),
StreamBuilder<Object>(
stream: bloc.phoneNumber,
builder: (context, snapshot) {
return TextField(
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: "Phone Number",
labelText: "Phone Number",
errorText: snapshot.error,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// onChanged: (value) => bloc.changeName,
onChanged: bloc.changePhoneNumber,
);
}),
],
),
),
),
actions: [
cancelButton,
_saveButton(),
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Widget _saveButton() {
final bloc = Provider.of<AddNewRelativeBloc>(context, listen: false);
return StreamBuilder<Object>(
stream: bloc.isValid,
builder: (context, snapshot) {
return GestureDetector(
onTap: snapshot.hasError || !snapshot.hasData
? null
: () {
bloc.submit();
},
child: Container(
height: 40,
width: 130,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: snapshot.hasError || !snapshot.hasData
? Color(0xffff69b4)
: Colors.green,
),
child: Text(
"Save",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
);
});
}
}
//My Future for getting response from api
Future<List> getRelativesList() async {
final response =
await http.get("$baseUrl");
Map<String, dynamic> map = json.decode(response.body);
List<dynamic> data = map["data"];
print("first id on console" + data[0]["id"].toString());
return list = data;
}
//Here is my model
class Relative {
int _code;
String _message;
List<Data> _data;
Relative({int code, String message, List<Data> data}) {
this._code = code;
this._message = message;
this._data = data;
}
int get code => _code;
set code(int code) => _code = code;
String get message => _message;
set message(String message) => _message = message;
List<Data> get data => _data;
set data(List<Data> data) => _data = data;
Relative.fromJson(Map<String, dynamic> json) {
_code = json['code'];
_message = json['message'];
if (json['data'] != null) {
_data = new List<Data>();
json['data'].forEach((v) {
_data.add(new Data.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['code'] = this._code;
data['message'] = this._message;
if (this._data != null) {
data['data'] = this._data.map((v) => v.toJson()).toList();
}
return data;
}
}
class Data {
int _id;
int _patientId;
String _name;
String _age;
String _relation;
String _phoneNo;
Data(
{int id,
int patientId,
String name,
String age,
String relation,
String phoneNo}) {
this._id = id;
this._patientId = patientId;
this._name = name;
this._age = age;
this._relation = relation;
this._phoneNo = phoneNo;
}
enter code here
int get id => _id;
set id(int id) => _id = id;
int get patientId => _patientId;
set patientId(int patientId) => _patientId = patientId;
String get name => _name;
set name(String name) => _name = name;
String get age => _age;
set age(String age) => _age = age;
String get relation => _relation;
set relation(String relation) => _relation = relation;
String get phoneNo => _phoneNo;
set phoneNo(String phoneNo) => _phoneNo = phoneNo;
Data.fromJson(Map<String, dynamic> json) {
_id = json['id'];
_patientId = json['patient_id'];
_name = json['name'];
_age = json['age'];
_relation = json['relation'];
_phoneNo = json['phone_no'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this._id;
data['patient_id'] = this._patientId;
data['name'] = this._name;
data['age'] = this._age;
data['relation'] = this._relation;
data['phone_no'] = this._phone`enter code here`No;
return data;
}
}
You return DataTable as an item for ListView.builder, that is why you see header and row. If you want display only table then remove ListView widget and return DataTable with rows. Something like
FutureBuilder(
builder: (context, snapshot) {
return DataTable(
columns: <DataColumn>[
...
],
rows: list.map((item) {
return DataRow(
cells: <DataCell>[...]
);
}),
),
}
)