Flutter switch toggles off on its own - android

I have created a flutter starter kit that allowes me to switch between themes in-app.
Now this works great but there is a small issue.
I have placed the switch button in the settings screen,
but when I go to another screen and then back to the settings screen
- it would seem that the switch button goes to Off mode on its own.
I'm attaching an image and my code on github so that you guys can see what I mean.
class ThemeSwitchState extends State {
bool switchControl = false;
Future<bool> darkMode() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
return sharedPreferences.getBool("isDark");
}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: darkMode(),
builder: (context, _snapshot) {
return Transform.scale(
scale: 1.5,
child: Switch(
value: _snapshot.data ?? false,
onChanged: (value) {
save(value);
setState(() {
});
},
activeColor: CustomColors().duskBlue,
activeTrackColor: CustomColors().noroGrey,
inactiveThumbColor: CustomColors().lureGrey,
inactiveTrackColor: CustomColors().noroGrey,
),
);
},
);
}
void save(bool value) async{
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setBool("isDark", value);
}
void toggleSwitch(bool value) {
ThemeChanger _themeChanger =
Provider.of<ThemeChanger>(context, listen: false);
if (switchControl == false) {
setState(() {
switchControl = true;
});
print('Theme is Dark');
_themeChanger.setTheme(CustomThemes.darkTheme.copyWith(
textTheme:
CustomThemes.darkTextTheme(CustomThemes.darkTheme.textTheme)));
} else {
setState(() {
switchControl = false;
});
print('Theme is Light');
_themeChanger.setTheme(CustomThemes.lightTheme.copyWith(
textTheme:
CustomThemes.lightTextTheme(CustomThemes.lightTheme.textTheme)));
}
}
}

You can use SharedPreferences from https://pub.dev/packages/shared_preferences
to save the last value of the CheckBox
I wrote an example
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Settings extends StatefulWidget {
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
Future<bool> darkMode() async{
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
return sharedPreferences.getBool("isNight");
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text("Night Mode"),
FutureBuilder<bool>(
future: darkMode(),
builder: (context, _snapshot){
return Checkbox(
value: _snapshot.data ?? false,
onChanged: (value){
save(value);
setState(() {
toggleSwitch(value);
});
},
);
},
)
],
),
),
);
}
void save(bool value) async{
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setBool("isNight", value);
}
}
EDIT: WITH SWITCH
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Settings extends StatefulWidget {
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
Future<bool> darkMode() async{
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
return sharedPreferences.getBool("isNight");
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text("Night Mode"),
FutureBuilder<bool>(
future: darkMode(),
builder: (context, _snapshot){
return Switch(
value: _snapshot.data ?? false,
onChanged: (value){
save(value);
setState(() {
});
},
);
},
)
],
),
),
);
}
void save(bool value) async{
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setBool("isNight", value);
}
}
UPDATE use value instead of switchControl
void toggleSwitch(bool value) {
ThemeChanger _themeChanger =
Provider.of<ThemeChanger>(context, listen: false);
if (value) {
setState(() {
switchControl = true;
});
print('Theme is Dark');
_themeChanger.setTheme(CustomThemes.darkTheme.copyWith(
textTheme:
CustomThemes.darkTextTheme(CustomThemes.darkTheme.textTheme)));
} else {
setState(() {
switchControl = false;
});
print('Theme is Light');
_themeChanger.setTheme(CustomThemes.lightTheme.copyWith(
textTheme:
CustomThemes.lightTextTheme(CustomThemes.lightTheme.textTheme)));
}
}
}

Related

Voice Commands On Flutter Application

I tried to do give a command to the flutter application using voice commands and in the application there was a text to voice function. so when i tried to give read command using voice there was a repeat on that function. how to avoid the repeat.
I have tried to terminate the Listening function after getting one input but it dosen't work.
Tried to terminate the Listening function.
but same error
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:newsapp/screens/News_Screen.dart';
import 'package:newsapp/utils/app_colors.dart';
import 'package:newsapp/utils/assets_constants.dart';
import 'package:webfeed/webfeed.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_speech/flutter_speech.dart';
const languages = const [
// const Language('Francais', 'fr_FR'),
const Language('English', 'en_US'),
const Language('Pусский', 'ru_RU'),
const Language('Italiano', 'it_IT'),
const Language('Español', 'es_ES'),
];
class Language {
final String name;
final String code;
const Language(this.name, this.code);
}
class BBCNews extends StatefulWidget {
#override
_BBCNewsState createState() => _BBCNewsState();
}
class _BBCNewsState extends State<BBCNews> {
late SpeechRecognition _speech;
FlutterTts flutterTts = FlutterTts();
bool _speechRecognitionAvailable = false;
bool _isListening = false;
String transcription = '';
String _currentLocale = 'en_US';
//Language selectedLang = languages.first;
#override
initState() {
super.initState();
activateSpeechRecognizer();
}
void activateSpeechRecognizer() {
//print('_MyAppState.activateSpeechRecognizer... ');
_speech = SpeechRecognition();
_speech.setAvailabilityHandler(onSpeechAvailability);
_speech.setRecognitionStartedHandler(onRecognitionStarted);
_speech.setRecognitionResultHandler(onRecognitionResult);
_speech.setRecognitionCompleteHandler(onRecognitionComplete);
_speech.setErrorHandler(errorHandler);
_speech.activate('en_US').then((res) {
setState(() => _speechRecognitionAvailable = res);
});
}
var client = http.Client();
Future myDevBlog() async {
var response = await client
.get(Uri.parse('http://feeds.bbci.co.uk/news/world/rss.xml'));
var channel = RssFeed.parse(response.body);
final item = channel.items;
return item;
}
Future<void> openFeed(String url) async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: false,
);
return;
}
}
rightIcon() {
return const Icon(
Icons.keyboard_arrow_right,
color: Colors.grey,
size: 30,
);
}
#override
Widget build(BuildContext context) {
var currentIndex;
return Scaffold(
appBar: AppBar(
backgroundColor: AppColors.primaryColor,
//title: const Text('VoiceNews'),
title: Image.asset(
AssetConstants.iconPath,
width: 150,
),
elevation: 10.0,
actions: [
IconButton(
onPressed: _speechRecognitionAvailable && !_isListening
? () => start()
: null,
icon: const Icon(Icons.play_arrow_rounded)),
IconButton(
onPressed: _isListening ? () => stop() : null,
icon: const Icon(Icons.stop_circle))
],
),
body: StreamBuilder(
stream: myDevBlog().asStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, int index) {
if (transcription.toLowerCase() == "play") {
// _isListening = false;
//stop();
print(transcription);
speak(snapshot.data[1].title);
var currentIndex = 0;
} else if (transcription.toLowerCase() == "next") {
// _isListening = false;
speak(snapshot.data[2].title);
print(transcription);
} else if (transcription.toLowerCase() == "all") {
// _isListening = false;
errorHandler();
//speak("Wrong Input");
//cancel();
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: ListTile(
title: Text(snapshot.data[index].title),
subtitle:
Text(snapshot.data[index].description),
//onTap: () =>
speak(snapshot.data[index].title),
// onTap: () =>
openFeed(snapshot.data[index].link),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
newsScreen(snapshot.data[index].link),
// Pass the arguments as part of the
RouteSettings. The
// DetailScreen reads the arguments
from these settings.
// settings: RouteSettings(
// arguments: todos[index],
// ),
),
);
print(snapshot.data[index].link);
},
onLongPress: () =>
speak(snapshot.data[index].description),
leading: Text((1 + index).toString()),
trailing: rightIcon(),
),
),
);
},
);
} else if (snapshot.hasError ||
snapshot.connectionState == ConnectionState.none) {
return Center(
child: Text(snapshot.error.toString()),
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
);
}
// List<CheckedPopupMenuItem<Language>> get
_buildLanguagesWidgets => languages
void start() => _speech.activate(_currentLocale).then((_) {
return _speech.listen().then((result) {
print('_MyAppState.start => result $result');
setState(() {
_isListening = result;
});
});
});
void cancel() =>
_speech.cancel().then((_) => setState(() => _isListening =
false));
void stop() => _speech.stop().then((_) {
setState(() => _isListening = false);
});
void onSpeechAvailability(bool result) =>
setState(() => _speechRecognitionAvailable = result);
// void onCurrentLocale(String locale) {
// // print('_MyAppState.onCurrentLocale... $locale');
// setState(
// () => _currentLocale = languages.firstWhere((l) =>
l.code == locale));
// }
void onRecognitionStarted() {
setState(() => _isListening = true);
}
void onRecognitionResult(String text) {
// print('_MyAppState.onRecognitionResult... $text');
setState(() => transcription = text);
}
void onRecognitionComplete(String text) {
//print('_MyAppState.onRecognitionComplete... $text');
setState(() => _isListening = false);
}
void errorHandler() => activateSpeechRecognizer();
void speak(String text) async {
await flutterTts.speak(text);
await flutterTts.setLanguage("en-US");
await flutterTts.setPitch(1);
await flutterTts.setSpeechRate(0.6);
}
}

Flutter Shared Preferences

Guys how to transfer data when I login.. For example when I login the transfer data are User ID, email and password.. So if I want to add something that requires the user id how do I call it?
This is my login method
void login() async {
if(passController.text.isNotEmpty && emailController.text.isNotEmpty) {
var response = await http.post(
Uri.parse("url"),
body: ({
'email': emailController.text,
'password': passController.text,
}));
if (response.statusCode == 200) {
final body = jsonDecode(response.body);
print(body.toString());
List<dynamic> data = body["Details"];
print(data[0]['email']);
pageRoute(data[0]['name']);
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Wrong password")));
}
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Sila isi tempat kosong")));
}
}
void pageRoute(data) async {
//STORE VALUE IN SHARED PREFERENCES
SharedPreferences pref = await SharedPreferences.getInstance();
await pref.setString("login", data);
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => nav()),
(route) => false);
This is where I want to take the user id without put it on the textfield.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import '../Utils/lib.dart';
class CarList extends StatefulWidget {
const CarList({Key? key}) : super(key: key);
#override
State<CarList> createState() => _CarListState();
}
class _CarListState extends State<CarList> {
var userController = TextEditingController();
// late SharedPreferences logindata;
// String? Email;
final pref = Pref();
// late String name;
// late String registeration_no;
#override
void initState() {
// TODO: implement initState
super.initState();
initial();
}
void initial() async {
if (pref.isLogin()) {
final body = jsonDecode(pref.getLogin());
print(body['Details']['user_id'].toString());
}
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
Container(
padding: const EdgeInsets.all(10),
child: TextField(
controller: userController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
suffixIcon: Icon(Icons.email),
labelText: 'User ID',
),
),
),
Container(
margin: EdgeInsets.all(25),
child: TextButton(
child: Text('test',
style: TextStyle(fontSize: 20.0,
color: Colors.blueAccent,
backgroundColor: Colors.white),
),
onPressed: () {
list();
},
),
)
]
)
);
}
void list() async {
{
var response = await http.post(
Uri.parse("url"),
body: ({
'user_id': userController.text,
}));
if (response.statusCode == 200) {
final body = jsonDecode(response.body);
// final SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
// sharedPreferences.setString('email', emailController.text);
pref.saveLogin(true, response.body);
print(body.toString());
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Successfully Login")));
}
}
}
}
Im kinda new with this flutter so might thankful with your help
You can do something like this:
This is store data in pageRoute.
void login() async {
if (passController.text.isNotEmpty && emailController.text.isNotEmpty) {
var response = await http.post(
Uri.parse("http://servisjer.me-tech.com.my/api/Login"),
body: ({
'email': emailController.text,
'password': passController.text,
}));
if (response.statusCode == 200) {
final body = jsonDecode(response.body);
print(body.toString());
List<dynamic> data = body["Details"];
print(data[0]['email']);
pageRoute(Data(data[0]['email'], data[0]['user_id']));
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Wrong password")));
}
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Sila isi tempat kosong")));
}
}
void pageRoute(Data data) async {
MySharedPreferences().storeUser(data);
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => nav()), (route) => false);
}
MySharedPreferences
class MySharedPreferences {
Future<void> storeUser(Data data) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("email", data.email);
prefs.setInt("user_id", data.userId);
}
Future getUserId() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var userId = prefs.getInt("user_id");
return userId;
}
}
In CarList, retrieve data using this way
#override
void initState() {
MySharedPreferences().getUserId().then((value) {
setState(() {
userController.text = value.toString();
});
});
//TODO: IMPLEMENT INITSTATE
super.initState();
}
Data
class Data {
var email;
var userId;
Data(this.email,this.userId);
}

How can I change a background screen with a photo fetched from api in flutter?

I'm fetching images from https://pixabay.com/api/docs/ this web-page.
I'm trying to save that image with onTap() {} with Shared Preferences package and set as background on previous page. I tried to make Shared preferences class seperate but it was showing errors. So pref.setString() method I did in ontap() {} and pref.getString() in my Center() widget with ternary operator to show that if image url is empty, write text, if it contains image url, show image. I tried to put it into setState() {} method but there are all red squiggles. How can I achieve this?
Thank you very much in advance.
Here is my code:
this is the page I'm trying to set background image:
import 'package:abcde/authentication_screen/pages/photos_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AfterAuth extends StatefulWidget {
AfterAuth({Key? key}) : super(key: key);
static const String pathId = 'Welcome Page';
#override
State<AfterAuth> createState() => _AfterAuthState();
}
class _AfterAuthState extends State<AfterAuth> {
String welcomeScreen = 'Welcome to the club';
#override
void initState() {
// TODO: implement initState
super.initState();
//final prefs = await SharedPreferences.getInstance();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Welcome'),
),
drawer: Drawer(
backgroundColor: Colors.white,
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.lightBlueAccent,
),
child: Text('Welcome'),
),
ListTile(
title: const Text(
'Bonjour',
),
onTap: () {
setState(() {
welcomeScreen = 'Bonjour';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Hello'),
onTap: () {
setState(() {
welcomeScreen = 'Hello';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Guten Tag'),
onTap: () {
setState(() {
welcomeScreen = 'Guten Tag';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Au Revoir'),
onTap: () {
setState(() {
welcomeScreen = 'Au Revoir';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Chao Bambino'),
onTap: () {
setState(() {
welcomeScreen = 'Chao Bambino';
});
Navigator.pop(context);
},
),
],
),
),
body: Center(
child: address == ''
? Text(welcomeScreen)
: Image(
image: NetworkImage(address),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlue,
onPressed: () {
Navigator.pushNamed(context, PhotosPage.pathId);
},
child: const Icon(
Icons.add,
color: Colors.white,
),
),
);
}
String address = '';
internalMemory() async {
final prefs = await SharedPreferences.getInstance();
address = prefs.getString('urlAddress')!;
/* if(address!.isNotEmpty) {
isAddressEmpty = false;
}*/
return address;
}
}
this is the page where I fetch images from api and trying to save with Shared Preferences. actually not sure if it saves and not sure how to check it:
import 'package:flutter/material.dart';
import '../../services/photos_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PhotosPage extends StatefulWidget {
const PhotosPage({Key? key}) : super(key: key);
static const String pathId = 'Photos page';
#override
State<PhotosPage> createState() => _PhotosPageState();
}
class _PhotosPageState extends State<PhotosPage> {
PhotosService photosService = PhotosService();
#override
void initState() {
super.initState();
photosService.getPhotos();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Photos'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
_getProductTypeList(),
]),
),
);
}
Widget _buildListItem(
BuildContext context, AsyncSnapshot<dynamic> snapshot, int index) {
final photoItem = snapshot.data[index].previewURL;
print('photoItem is $photoItem');
return photoCard(context, photoItem);
}
Widget _buildList(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
var values = snapshot.data;
return ListView.builder(
itemCount: snapshot.hasData ? snapshot.data.length : 0,
itemBuilder: (context, index) {
return _buildListItem(context, snapshot, index);
},
);
}
Widget _getProductTypeList() {
return Expanded(
child: FutureBuilder(
future: photosService.getPhotos(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: LinearProgressIndicator(),
);
}
return _buildList(context, snapshot);
},
),
);
}
}
Widget photoCard(BuildContext context, String url) {
return GestureDetector(
onTap: () async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('urlAddress', url);
const snackBar =
SnackBar(content: Text('You saved image'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
child: Card(
child: Image(
image: NetworkImage(url),
),
),
);
}
this is my service/model
import 'dart:convert';
import 'package:http/http.dart';
const apiKey = '26711456-bde74f403cb42e77029bc1678';
const appUrl = 'https://pixabay.com/api/';
class PhotosService {
Future getData(String url) async {
print('Calling url: $url');
final response = await get(Uri.parse(url));
if (response.statusCode == 200) {
return response.body;
} else {
print(response.statusCode);
}
}
String? query;
List<PhotosModel> dataList = [];
Future<List<PhotosModel>> getPhotos() async {
final photosData = await getData('$appUrl?key=$apiKey&q=$query');
var data = json.decode(photosData);
var items = data["hits"];
items.forEach((element) {
dataList.add(PhotosModel.fromJson(element));
});
print('this is photos data: $photosData');
return dataList;
}
}
class PhotosModel {
String previewURL;
PhotosModel({required this.previewURL});
factory PhotosModel.fromJson(Map<dynamic, dynamic> json) =>
_commentFromJson(json);
Map<dynamic, dynamic> toJson() => photosModelToJson(this);
}
PhotosModel _commentFromJson(Map<dynamic, dynamic> json) {
return PhotosModel(
previewURL: json['previewURL'],
);
}
Map<dynamic, dynamic> photosModelToJson(PhotosModel instance) =>
<dynamic, dynamic>{
'previewURL': instance.previewURL,
};
Call your function on initState to set the background on restart the app.
#override
void initState() {
// TODO: implement initState
super.initState();
internalMemory();
//final prefs = await SharedPreferences.getInstance();
}
add setstate to your internalMemory() function to set background
internalMemory() async {
final prefs = await SharedPreferences.getInstance();
address = await prefs.getString('urlAddress')!;
setState((){});
}
also, add the function below to set the background when you navigate back.
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlue,
onPressed: () async {
await Navigator.pushNamed(context, PhotosPage.pathId);
internalMemory();
},
child: const Icon(
Icons.add,
color: Colors.white,
),
),

Flutter - How to persist selected language?

I folowed this video to implement localization in my app :- https://youtu.be/Zw4KoorVxgg
Everything works fine but when the app is relaunched the language gets set to default. How can I persiste the data.
[Note:- I am already using shared preference to persist some other data in my app, but I have no clue about persisting selected Locale]
This class below is called to set the locale:
class LocaleProvider extends ChangeNotifier {
Locale? _locale;
Locale? get locale => _locale;
void setLocale(Locale locale) {
if (!L10n.all.contains(locale)) return;
_locale = locale;
notifyListeners();
}
void clearLocale() {
_locale = null;
notifyListeners();
}
}
And this is the screen where I set the language:
class SelectLanguage extends StatefulWidget {
const SelectLanguage({Key? key}) : super(key: key);
#override
_SelectLanguageState createState() => _SelectLanguageState();
}
class _SelectLanguageState extends State<SelectLanguage> {
void setLocale(String selectedLocale) {
final provider = Provider.of<LocaleProvider>(context, listen: false);
provider.setLocale(
Locale(selectedLocale),
);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
body: Padding(
padding:
EdgeInsets.all(MediaQuery.of(context).size.width / 41.1),
child: GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2.75,
crossAxisSpacing: MediaQuery.of(context).size.width / 41.1,
mainAxisSpacing: MediaQuery.of(context).size.width / 41.1,
),
children: [
LanguageTile(
tileTitle: 'English',
titleOnTap: () => setLocale('en'),
),
LanguageTile(
tileTitle: 'हिन्दी',
titleOnTap: () => setLocale('hi'),
),
],
),
),
),
);
}
}
using shared preference
static Future<Locale> setLocale(String langCode) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(languageCode, langCode);
return locale(langCode);
}
static Locale locale(String languageCode) {
Locale _temp;
if (languageCode == 'hi') {
_temp = Locale('hi');
}
else {
_temp = Locale('en');
}
return _temp;
}
static Future<Locale> getLocale() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String code = prefs.getString(languageCode);
print('language code:$code');
return locale(code);
}
create a class
import 'dart:ui';
import 'package:shared_preferences/shared_preferences.dart';
const String LAGUAGE_CODE = 'languageCode';
//languages code
const String ENGLISH = 'en';
const String FRENCH = 'fr';
const String ARABIC = 'ar';
const String KINYARWANDA = 'rw';
const String SWAHILLI ="sw";
Future<Locale> storeLocale(String languageCode) async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
await _prefs.setString(LAGUAGE_CODE, languageCode);
return _locale(languageCode);
}
Future<Locale> getLocale() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
String languageCode = _prefs.getString(LAGUAGE_CODE) ?? ENGLISH;
return _locale(languageCode);
}
Locale _locale(String languageCode) {
switch (languageCode) {
case ENGLISH:
return const Locale(ENGLISH, '');
case FRENCH:
return const Locale(FRENCH, "");
case ARABIC:
return const Locale(ARABIC, "");
case KINYARWANDA:
return const Locale(KINYARWANDA, "");
case SWAHILLI:
return const Locale(SWAHILLI, "");
default:
return const Locale(ENGLISH, '');
}
}
then in your provider class add
Future<void> setLocale(Locale locale) async{
if(!L10n.all.contains(locale)) return;
_locale =locale;
storeLocale(locale.languageCode);//add this important
// storelocal(locale.languageCode);
notifyListeners();
}
where or widget you are implementing it e.g dropdown
Widget build(BuildContext context) {
final provider = Provider.of<LocaleProvider>(context,);
final locale = provider.locale ?? const Locale('en');
return DropdownButtonHideUnderline(
child: DropdownButton(
value: locale,
icon: const Icon(Icons.language,color:Colors.white,),
items: L10n.all.map((locale) {
final flag = L10n.getFlag(locale.languageCode);
return DropdownMenuItem(
value: locale,
onTap: ()async {
//await storeLocale(locale.languageCode);//may uncomment this
final provider =
Provider.of<LocaleProvider>(context,listen: false );
provider.setLocale(locale);
},
and in the main.dart change to stateful and override
#override
void didChangeDependencies() {
super.didChangeDependencies();
getLocale().then(
(locale) => Provider.of<LocaleProvider>(context,listen: false).setLocale(locale));
}
hope this helps

flutter search bar with bloc with firestore

I do search app bar.I search data by title(substring) and return items.I have Ui,bloc,service classes.I dont know how to pass '$searchQuery' into bloc sink.I return stream list(in Ui),but firstly i need to filter my list by title(item) and put in sink.Could you give me some advise or examples?Should i fix my bloc class?
class Search extends StatefulWidget {
#override
_Search createState() => _Search();
}
class _Search extends State<Search> {
static final GlobalKey<ScaffoldState> scaffoldKey =
new GlobalKey<ScaffoldState>();
TextEditingController _searchQuery;
bool _isSearching = false;
String searchQuery = "Search query";
#override
void initState() {
super.initState();
_searchQuery = new TextEditingController();
}
void _startSearch() {
print("open search box");
ModalRoute.of(context)
.addLocalHistoryEntry(new LocalHistoryEntry(onRemove: _stopSearching));
setState(() {
_isSearching = true;
});
}
void _stopSearching() {
_clearSearchQuery();
setState(() {
_isSearching = false;
});
}
void _clearSearchQuery() {
print("close search box");
setState(() {
_searchQuery.clear();
updateSearchQuery("Search query");
});
}
Widget _buildTitle(BuildContext context) {
var horizontalTitleAlignment =
Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start;
return new InkWell(
onTap: () => scaffoldKey.currentState.openDrawer(),
child: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: horizontalTitleAlignment,
children: <Widget>[
const Text('Seach box'),
],
),
),
);
}
Widget _buildSearchField() {
return new TextField(
controller: _searchQuery,
autofocus: true,
decoration: const InputDecoration(
hintText: 'Search...',
border: InputBorder.none,
hintStyle: const TextStyle(color: Colors.white30),
),
style: const TextStyle(color: Colors.white, fontSize: 16.0),
onChanged: updateSearchQuery,
);
}
void updateSearchQuery(String newQuery) {
setState(() {
searchQuery = newQuery;
});
print("search query " + newQuery);
}
List<Widget> _buildActions() {
if (_isSearching) {
return <Widget>[
new IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
if (_searchQuery == null || _searchQuery.text.isEmpty) {
Navigator.pop(context);
return;
}
_clearSearchQuery();
},
),
];
}
return <Widget>[
new IconButton(
icon: const Icon(Icons.search),
onPressed: _startSearch,
),
];
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: scaffoldKey,
appBar: new AppBar(
leading: _isSearching ? const BackButton() : null,
title: _isSearching ? _buildSearchField() : _buildTitle(context),
actions: _buildActions(),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'$searchQuery',
style: Theme.of(context).textTheme.display1,
)
],
),
),
));
}
}
class MovieService {
static final String _baseUrl = 'xxxx';
final CollectionReference _db;
MovieService() : _db = Firestore.instance.collection(_baseUrl);
Future<List<MovieEntity>> searchByString(String stringSearch) async {
final CollectionReference _dbs = Firestore.instance.collection(_baseUrl);
QuerySnapshot query =
await _dbs.where("Title", isEqualTo: stringSearch.substring(0, 1).toUpperCase()).getDocuments();
List<MovieEntity> products = query.documents
.map((doc) => MovieEntity.fromSnapshotJson(doc))
.toList();
return products;
}
}
class MovieBloc extends BlocBase {
String nameFilmSearchQuery;
final MovieService _productService;
MovieBloc(this._productService) {
_loadMovies();
}
final BehaviorSubject<List<MovieEntity>> _controllerSearch =
new BehaviorSubject<List<MovieEntity>>.seeded(List<MovieEntity>());
Observable<List<MovieEntity>> get listMoviesFluxSearch =>
_controllerSearch.stream;
Sink<List<MovieEntity>> get listMoviesEventSearch => _controllerSearch.sink;
_loadMovies() async {
listMoviesEventSearch.add(await _productService.searchByString(nameFilmSearchQuery));// i should pass '$searchQuery' here from Ui
}
#override
void dispose() {
_controllerSearch.close();
super.dispose();
}
}
A possible implementation of this Feature, includes a SearchDelegate used relatively to the main BLoC that changes states and events.
In this case the Resource needed is a City, so it is implemented a "SearchCity" class that extends SearchDelegate.
Overriding the method buildResults and BlocBuilder, it also adds a SearchCity event and some SearchCity states, in order to easily manage all possible operations related to the search operation itself.
class CitySearchEvent {
final String query;
const CitySearchEvent(this.query);
#override
String toString() => 'CitySearchEvent { query: $query }';
}
class CitySearchState {
final bool isLoading;
final List<City> cities;
final bool hasError;
const CitySearchState({this.isLoading, this.cities, this.hasError});
factory CitySearchState.initial() {
return CitySearchState(
cities: [],
isLoading: false,
hasError: false,
);
}
factory CitySearchState.loading() {
return CitySearchState(
cities: [],
isLoading: true,
hasError: false,
);
}
factory CitySearchState.success(List<City> cities) {
return CitySearchState(
cities: cities,
isLoading: false,
hasError: false,
);
}
factory CitySearchState.error() {
return CitySearchState(
cities: [],
isLoading: false,
hasError: true,
);
}
#override
String toString() =>
'CitySearchState {cities: ${cities.toString()}, isLoading: $isLoading, hasError: $hasError }';
}
Here is the edits added to the main BLoC implementation, that uses previously created States and Events in order to contruct a better business logic for the operation.
class CityBloc extends Bloc<CitySearchEvent, CitySearchState> {
#override
CitySearchState get initialState => CitySearchState.initial();
#override
void onTransition(Transition<CitySearchEvent, CitySearchState> transition)
{
print(transition.toString());
}
#override
Stream<CitySearchState> mapEventToState(CitySearchEvent event) async* {
yield CitySearchState.loading();
try {
List<City> cities = await _getSearchResults(event.query);
yield CitySearchState.success(cities);
} catch (_) {
yield CitySearchState.error();
}
}
Future<List<City>> _getSearchResults(String query) async {
// Simulating network latency
await Future.delayed(Duration(seconds: 1));
return [City('Chicago'), City('Los Angeles')];
}
}
Here is a full example of a single-file implementation that includes both UI and search methods.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider(
create: (_) => CityBloc(),
child: MyHomePage(),
));
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Search Delegate'),
),
body: Container(
child: Center(
child: RaisedButton(
child: Text('Show search'),
onPressed: () async {
City selected = await showSearch<City>(
context: context,
delegate: CitySearch(BlocProvider.of<CityBloc>(context)),
);
print(selected);
},
),
),
),
);
}
}
class City {
final String name;
const City(this.name);
#override
String toString() => 'City { name: $name }';
}
class CitySearch extends SearchDelegate<City> {
final Bloc<CitySearchEvent, CitySearchState> cityBloc;
CitySearch(this.cityBloc);
#override
List<Widget> buildActions(BuildContext context) => null;
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: BackButtonIcon(),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
cityBloc.add(CitySearchEvent(query));
return BlocBuilder(
bloc: cityBloc,
builder: (BuildContext context, CitySearchState state) {
if (state.isLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
if (state.hasError) {
return Container(
child: Text('Error'),
);
}
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.location_city),
title: Text(state.cities[index].name),
onTap: () => close(context, state.cities[index]),
);
},
itemCount: state.cities.length,
);
},
);
}
#override
Widget buildSuggestions(BuildContext context) => Container();
}
class CitySearchEvent {
final String query;
const CitySearchEvent(this.query);
#override
String toString() => 'CitySearchEvent { query: $query }';
}
class CitySearchState {
final bool isLoading;
final List<City> cities;
final bool hasError;
const CitySearchState({this.isLoading, this.cities, this.hasError});
factory CitySearchState.initial() {
return CitySearchState(
cities: [],
isLoading: false,
hasError: false,
);
}
factory CitySearchState.loading() {
return CitySearchState(
cities: [],
isLoading: true,
hasError: false,
);
}
factory CitySearchState.success(List<City> cities) {
return CitySearchState(
cities: cities,
isLoading: false,
hasError: false,
);
}
factory CitySearchState.error() {
return CitySearchState(
cities: [],
isLoading: false,
hasError: true,
);
}
#override
String toString() =>
'CitySearchState {cities: ${cities.toString()}, isLoading: $isLoading, hasError: $hasError }';
}
class CityBloc extends Bloc<CitySearchEvent, CitySearchState> {
#override
CitySearchState get initialState => CitySearchState.initial();
#override
void onTransition(Transition<CitySearchEvent, CitySearchState> transition) {
print(transition.toString());
}
#override
Stream<CitySearchState> mapEventToState(CitySearchEvent event) async* {
yield CitySearchState.loading();
try {
List<City> cities = await _getSearchResults(event.query);
yield CitySearchState.success(cities);
} catch (_) {
yield CitySearchState.error();
}
}
Future<List<City>> _getSearchResults(String query) async {
// Simulating network latency
await Future.delayed(Duration(seconds: 1));
return [City('Chicago'), City('Los Angeles')];
}
}
A reference for the code above is available at this Github Gist link
https://gist.github.com/felangel/11769ab10fbc4076076299106f48fc95

Categories

Resources