I‘m using a Stream with SearchDelegate.
When I enter something in the search field, it's working fine. But as soon as I press the search symbol, it displays nothing, because the stream is empty. How is that possible?
Here is some code:
List<Notes> notes;
...
void search() {
showSearch(
context: context,
delegate: Search(
Stream.value(UnmodifiableListView<Note>(notes)).asBroadcastStream()
)
}
class Search extends SearchDelegate{
final Stream<UnmodifiableListView<Note>> notes;
Search(this.notes);
...
#override
Widget buildResults(BuildContext context){
return _buildStreamBuilder();
}
#override
Widget buildSuggestions(BuildContext context){
return _buildStreamBuilder();
}
StreamBuilder<UnmodifiableListView<Note>> _buildStreamBuilder() {
return StreamBuilder< UnmodifiableListView<Note>> (
stream: notes
builder: (context, AsyncSnapshot< UnmodifiableListView<Note>>) {
final results = snapshot.data.where((note){
....
}
}
Why is the stream empty, when buildResults() is called, but not when buildSuggestions() is called?
Edit in the abstract class SearchDelegate:
void showResults(BuildContext context) {
_focusNode?.unfocus();
_currentBody = _SearchBody.results;
}
Replace:
void showResults(BuildContext context) {
if(_queryTextController.text.isNotEmpty) {
_focusNode?.unfocus();
_currentBody = _SearchBody.results;
}else{
_focusNode?.unfocus();
}
}
It worked for me
Related
everyone I have this error. I'm trying to save favourite things on my App. But context is giving me an error.
The instance member 'context' can't be accessed in an initializer. (Documentation) Try replacing the reference to the instance member with a different expression
mixin _$WishListStore on _WishListStore, Store {
final _$wishListAtom =
Atom(name: '_WishListStore.wishList', context: context);
#override
List<Category> get wishList {
_$wishListAtom.reportRead();
return super.wishList;
}
#override
set wishList(List<Category> value) {
_$wishListAtom.reportWrite(value, super.wishList, () {
super.wishList = value;
});
}
final _$isNetworkAvailableAtom =
Atom(name: '_WishListStore.isNetworkAvailable', context: context);
#override
bool get isNetworkAvailable {
_$isNetworkAvailableAtom.reportRead();
return super.isNetworkAvailable;
}
#override
set isNetworkAvailable(bool value) {
_$isNetworkAvailableAtom.reportWrite(value, super.isNetworkAvailable, () {
super.isNetworkAvailable = value;
});
}
final _$addToWishListAsyncAction =
AsyncAction('_WishListStore.addToWishList', context: context);
red line: context: context);
I'm working on a launcher app with gestures settings and I'm looking for a way to achieve two-finger swipe up and down gestures, i found out that it can be achieved by using RawGestureDetector and MultiDragGestureRecognizer , but I have no idea how to do it, can anyone share a code example or explain how to do it
i tried this code sample but it doesn't seem to work :
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class TwoFingerPointerWidget extends StatelessWidget {
final Widget child;
final OnUpdate onUpdate;
TwoFingerPointerWidget({required this.child, required this.onUpdate});
#override
Widget build(BuildContext context) {
return RawGestureDetector(
gestures: <Type, GestureRecognizerFactory>{
CustomVerticalMultiDragGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
CustomVerticalMultiDragGestureRecognizer>(
() => CustomVerticalMultiDragGestureRecognizer(debugOwner: null),
(CustomVerticalMultiDragGestureRecognizer instance) {
instance.onStart = (Offset position) {
return CustomDrag(events: instance.events, onUpdate: onUpdate);
};
},
),
},
child: child,
);
}
}
typedef OnUpdate(DragUpdateDetails details);
class CustomDrag extends Drag {
final List<PointerDownEvent> events;
final OnUpdate onUpdate;
CustomDrag({required this.events, required this.onUpdate});
#override
void update(DragUpdateDetails details) {
super.update(details);
final delta = details.delta;
if (delta.dy.abs() > 0 && events.length == 2) {
onUpdate.call(DragUpdateDetails(
sourceTimeStamp: details.sourceTimeStamp,
delta: Offset(0, delta.dy),
primaryDelta: details.primaryDelta,
globalPosition: details.globalPosition,
localPosition: details.localPosition,
));
}
}
#override
void end(DragEndDetails details) {
super.end(details);
}
}
class CustomVerticalMultiDragGestureRecognizer
extends MultiDragGestureRecognizer {
final List<PointerDownEvent> events = [];
CustomVerticalMultiDragGestureRecognizer({required Object? debugOwner})
: super(debugOwner: debugOwner);
#override
createNewPointerState(PointerDownEvent event) {
events.add(event);
return _CustomVerticalPointerState(event.position, onDisposeState: () {
events.remove(event);
});
}
#override
String get debugDescription => 'custom vertical multidrag';
}
typedef OnDisposeState();
class _CustomVerticalPointerState extends MultiDragPointerState {
final OnDisposeState onDisposeState;
_CustomVerticalPointerState(Offset initialPosition,
{required this.onDisposeState})
: super(initialPosition, PointerDeviceKind.touch, null);
#override
void checkForResolutionAfterMove() {
if (pendingDelta!.dy.abs() > kTouchSlop) {
resolve(GestureDisposition.accepted);
}
}
#override
void accepted(GestureMultiDragStartCallback starter) {
starter(initialPosition);
}
#override
void dispose() {
onDisposeState.call();
super.dispose();
}
}
So i just found a solution , not the best one but it still works ,for anyone looking
you have to work around the listener class , here is my code :
final events = [];
Listener(
onPointerDown: (event) {
events.add(event.pointer);
},
onPointerUp: (event) {
events.clear();
},
onPointerMove: (event) {
if (events.length == 2) {
int sensitivity = 8;
if (event.delta.dy > sensitivity) {
// code for two finger swipe up event
} else if (event.delta.dy < -sensitivity) {
// code for two finger swipe down event
}
}
},
AppScreenC will give all the installed apps in a ListView
class AppScreenC extends StatefulWidget {
#override
_AppScreenCState createState() => _AppScreenCState();
}
List<Application> apps;
getApps() async {
if (apps == null) {
apps = await DeviceApps.getInstalledApplications(
onlyAppsWithLaunchIntent: true,
includeSystemApps: true,
includeAppIcons: true);
apps.sort((a, b) => a.appName.compareTo(b.appName));
}
}
ListView with all the installed apps are getting displayed in screen. I'm trying to change the icons based on onTap event.
But clicking on a list, icons are not changing.
class _AppScreenCState extends State<AppScreenC> {
final _app = <Application>[];
#override
Widget build(BuildContext context) {
return _buildApps();
}
Widget _buildApps() {
getApps();
return ListView.builder(itemBuilder: (BuildContext context, int index) {
_app.addAll(apps);
return _buildRow(_app[index]);
});
}
Widget _buildRow(ApplicationWithIcon app) {
bool selected = false;
return ListTile(
leading: Image.memory(app.icon, height: 40),
trailing:
Icon(selected ? Icons.check_circle : Icons.check_circle_outline),
title: Text(app.appName),
onTap: () {
selected = !selected;
// print("$selected");
// print("${app.apkFilePath}");
setState(() {});
},
);
}
}
itemCount: missing in ListView.Builder
Widget _buildApps() {
getApps();
return ListView.builder(itemBuilder: (BuildContext context, int index) {
_app.addAll(apps);
return _buildRow(_app[index]);
}, itemCount: _app.length);
}
Also,
class _AppScreenCState extends State<AppScreenC> {
bool selected = false; // this should be at the top as it will persist the value
class _AppScreenCState extends State<AppScreenC> {
bool selected = false;
Widget _buildRow(ApplicationWithIcon app) {
//bool selected = false; not here
Even though I am using ViewModel, whenever the device is rotated, the data in the Recyclerview disappears. I had to put the makeSearch() method inside the onClick() method because I need to get the text that the button grabs and use it as the search parameter. Is there a better way I can handle this to avoid this problem? My code is right here:
SearchActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
// What happens when the search button is clicked
materialButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Objects.requireNonNull(textInputEditText.getText()).toString().isEmpty()) {
textInputEditText.setError("Type a search query");
} else {
mSearchInput = Objects.requireNonNull(textInputEditText.getText()).toString();
textInputEditText.setText("");
makeSearch();
}
}
});
}
// Gets the ViewModel, Observes the Question LiveData and delivers it to the Recyclerview
private void makeSearch() {
final SearchAdapter searchAdapter = new SearchAdapter();
SearchViewModel mSearchViewModel = new ViewModelProvider(this,
new CustomSearchViewModelFactory(new SearchRepository())).get(SearchViewModel.class);
mSearchViewModel.setQuery(mSearchInput);
mSearchViewModel.getQuestionLiveData().observe(this, new Observer<List<Question>>() {
#Override
public void onChanged(List<Question> questions) {
mQuestions = questions;
searchAdapter.setQuestions(questions);
}
});
mRecyclerView.setAdapter(searchAdapter);
searchAdapter.setOnClickListener(mOnClickListener);
}
SearchViewModel:
public class SearchViewModel extends ViewModel {
private SearchRepository mSearchRepository;
private MutableLiveData<String> mSearchLiveData = new MutableLiveData<>();
private LiveData<List<Question>> mQuestionLiveData = Transformations.switchMap(mSearchLiveData, (query) -> {
return mSearchRepository.getQuestions(query);
});
SearchViewModel(SearchRepository searchRepository) {
this.mSearchRepository = searchRepository;
}
public LiveData<List<Question>> getQuestionLiveData() {
return mQuestionLiveData;
}
public void setQuery(String query) {
mSearchLiveData.setValue(query);
}
}
SearchRepository:
public class SearchRepository {
//private String inTitle;
private MutableLiveData<List<Question>> mQuestions = new MutableLiveData<>();
public SearchRepository() {
//getQuestionsWithTextInTitle();
}
private void getQuestionsWithTextInTitle(String inTitle) {
ApiService apiService = RestApiClient.getApiService(ApiService.class);
Call<QuestionsResponse> call = apiService.getQuestionsWithTextInTitle(inTitle);
call.enqueue(new Callback<QuestionsResponse>() {
#Override
public void onResponse(Call<QuestionsResponse> call, Response<QuestionsResponse> response) {
QuestionsResponse questionsResponse = response.body();
if (questionsResponse != null) {
mQuestions.postValue(questionsResponse.getItems());
//shouldShowData = true;
} else {
Log.d("SearchRepository", "No matching question");
//shouldShowData = false;
}
}
#Override
public void onFailure(Call<QuestionsResponse> call, Throwable t) {
//shouldShowData = false;
t.printStackTrace();
}
});
}
public LiveData<List<Question>> getQuestions(String inTitle) {
getQuestionsWithTextInTitle(inTitle);
return mQuestions;
}
}
Your approach of passing the search input in through your CustomSearchViewModelFactory and into the constructor for the ViewModel and into the constructor for your SearchRepository isn't going to work in any case. While the first time you search your CustomSearchViewModelFactory creates the ViewModel, the second time you hit search, your SearchViewModel is already created and your factory is not invoked a second time, meaning you never get the second query.
Instead, you should file the ViewModel Overview documentation, and use Transformations.switchMap() to convert your input (the search string) into a new LiveData<List<Question>> for that given query.
This means that your ViewModel would look something like
public class SearchViewModel extends ViewModel {
private SearchRepository mSearchRepository;
private MutableLiveData<String> mSearchLiveData = new MutableLiveData<String>();
private LiveData<List<Question>> mQuestionLiveData =
Transformations.switchMap(mSearchLiveData, (query) -> {
return mSearchRepository.getQuestions(query);
});
public SearchViewModel() {
mSearchRepository = new SearchRepository();
}
public void setQuery(String query) {
mSearchLiveData.setValue(query);
}
public LiveData<List<Question>> getQuestionLiveData() {
return mQuestionLiveData;
}
}
You'd then update your Activity to:
Always observe the getQuestionLiveData() (note that you won't get a callback to your Observer until you actually set the first query)
Call setQuery() on your SearchViewModel in your makeSearch()
Remove your CustomSearchViewModelFactory entirely (it would no longer be needed).
I am new so excuse me if I dont ask a question right or post enough information.
I am new to creating mobile applications and i am using Xamarin.Forms to create a custom view. with this view I am using an Android ViewRenderer to play audio/video with built in android MediaPlayer/VideoView.
pretty much the exact same thing as the android renderer posted and accepted as the answer for Renderer I copied and is working
My issue is when the video starts and you click the homepage/back button the audio continues playing for a few seconds and then stops. I want to audio to stop immediately.
Methods I have tried:
In my ViewRenderer I have attempted to override SurfaceDestroyed to call player.stop(). This has not worked, no errors or anything just audio continues like this code doesnt exist. Audio stops after about 3-5 seconds.
In the ViewRenderer I have attempted to use the Control.SystemUiVisibilityChange event to call player.stop(). No errors or anything. Audio continues for 3-5 seconds.
I am unable to pass the player instance to the main activity onPause() method as I am to new to Xamarin.Forms and android ViewRenderers to understand how to. Possibly calling this player.stop() on the onPause() method will work but I cant find how to do this. Can anyone assist? I have searched many forums for weeks and have finally given up to post a question.
For back button, you simply need to override OnBackButtonPressed of your current Xamarin.Forms' page:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override bool OnBackButtonPressed()
{
//stop the videoview
videoview.Stop();
return base.OnBackButtonPressed();
}
...
}
For home button, I referred to this thread and made a Xamarin version of HomeWatcher out of Jack's answer:
public interface IOnHomePressedListener
{
void OnHomePressed();
void OnHomeLongPressed();
}
public class HomeWatcher
{
static readonly String TAG = "hg";
private Context mContext;
private IntentFilter mFilter;
private IOnHomePressedListener mListener;
private InnerRecevier mRecevier;
public HomeWatcher(Context context)
{
mContext = context;
mFilter = new IntentFilter(Intent.ActionCloseSystemDialogs);
}
public void SetOnHomePressedListener(IOnHomePressedListener listener)
{
mListener = listener;
mRecevier = new InnerRecevier(mListener);
}
public void StartWatch()
{
if (mRecevier != null)
{
mContext.RegisterReceiver(mRecevier, mFilter);
}
}
public void StopWatch()
{
if (mRecevier != null)
{
mContext.UnregisterReceiver(mRecevier);
}
}
private class InnerRecevier : BroadcastReceiver
{
readonly String SYSTEM_DIALOG_REASON_KEY = "reason";
readonly String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
readonly String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
readonly String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
IOnHomePressedListener _listener;
public InnerRecevier(IOnHomePressedListener listener)
{
_listener = listener;
}
public override void OnReceive(Context context, Intent intent)
{
String action = intent.Action;
if (action.Equals(Intent.ActionCloseSystemDialogs))
{
String reason = intent.GetStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (reason != null)
{
//Log.e(TAG, "action:" + action + ",reason:" + reason);
if (_listener != null)
{
if (reason.Equals(SYSTEM_DIALOG_REASON_HOME_KEY))
{
_listener.OnHomePressed();
}
else if (reason.Equals(SYSTEM_DIALOG_REASON_RECENT_APPS))
{
_listener.OnHomeLongPressed();
}
}
}
}
}
}
}
And use it in the VideoViewRenderer ( StartWatch() when video start play, StopWatch() when the videoview is cleaned):
public class VideoViewRenderer : ViewRenderer<VideoView, Android.Widget.VideoView>, ISurfaceHolderCallback,IOnHomePressedListener
{
...
private MediaPlayer _player;
private HomeWatcher _homeWatcher;
public VideoViewRenderer(Context context) : base(context)
{
_context = context;
_homeWatcher = new HomeWatcher(context);
_homeWatcher.SetOnHomePressedListener(this);
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomVideoViewDemo.VideoView> e)
{
base.OnElementChanged(e);
e.NewElement.CleanAction = new Action(() =>
{
#region Clean video player action (player no more used)
if (_player == null)
return;
//stop watch home button
_homeWatcher.StopWatch();
_player.Release();
#endregion
});
e.NewElement.PlayAction = new Action(() =>
{
#region Play video if it was stopped
if (_player == null)
return;
//start watch home button
_homeWatcher.StartWatch();
if (!_player.IsPlaying)
{
_player.Start();
}
#endregion
});
...
}
}