I'm trying to test my cubit which have two repositories, SomethingRepository and AnotherRepository.
I'm using mocktail and bloc_test, and I'm having troubles to use two whens inside build parameters of bloc_test, always throwing TypeError.
...
final MockSomething mockObject1 = MockSomething();
final MockAnother mockAnother = MockAnother();
...
blocTest<DoSomethingCubit, DoSomethingState>(
'emits [InProgress, Success] state for successful do something',
build: () {
when(
() => somethingRepository.doSomethingWithId(mockObject1.id)
).thenAnswer(
(_) => Future.delayed(
const Duration(milliseconds: 1),
() => right(mockObject1)
)
);
when(
() => anotherRepository.create(mockAnother)
).thenAnswer(
(_) => Future.delayed(
const Duration(milliseconds: 1),
() => right(mockAnother)
)
);
return doSomethingCubit;
},
act:(cubit) => cubit.doSomething(mockObject1.id),
expect: () => [
const DoSomethingState.inProgress(),
const DoSomethingState.success(unit),
],
);
The return is something 'like type 'Null' is not a subtype of type 'Future<SomethingFailure, Unit>''
I tried to move the when method to setUp() method but nothing changes, just if this test I'm getting this problem, with another test with just one mock/when everything running ok.
I have below simple code which work fine in Android but in IOS await doesn't work.
const CheckAsyncfucntion = async () => {
console.log('Before Await execution');
const authenticateuser = await newUser(registration_id); // return true or false
console.log('After Await Execution => ', authenticateuser);
}
in andorid it work fine. but in ios both the log are getting print with undefined value of authenticateuser. and after that newUser value is getting return.
Any Lead in this will be appreciated.
Am using IOS smiulator iphone12 iOS 15.0
I guess the problem is in the function newUser.
My iOS simulator works just fine with any async functions like below.
const CheckAsyncfucntion = async () => {
console.log('Before Await execution');
// return true or false from Promise
const authenticateuser = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, 2000);
});
console.log('After Await Execution => ', authenticateuser);
};
CheckAsyncfucntion();
How do i dispatch a dynamically determined amount of times through redux?
I have users who are able to create lists of items and create as many as they like. When they navigate to an item page they can choose which lists to add it to.
This means that i may have to dispatch adding an item to one list OR MORE.
I want to dispatch the action to receive my updated lists only if all dispatches to 'add an item' to a list return a promise.
If i iterate through an array and pass in an argument to dispatch with is there a way to wait on a promise before continuing to the next step/array-index?
eg i'd need to call something like this several times but how many times will be determined by user and should only
export const addToList = (user_id, list_id, stock_ticker) => dispatch => {
return StockApiutil.addToList(user_id, list_id, stock_ticker)
.then(lists => dispatch(receiveLists(lists)))
};
export const addToAllLists = (user_id, list_ids, stock_ticker) => dispatch => {
dispatch(startListLoading());
list_ids.map( list_id =>
addToList(user_id, list_id, stock_ticker)
)
.then(dispatch(stopListLoading()))
}
This doesn't work because it doesn't return a promise and if i use a promise.all i won't create an array corresponding to final state for the lists.
You can do the following:
export const addToList = (
user_id,
list_id,
stock_ticker
) => (dispatch) => {
//you are returning a promise here, that is good
return StockApiutil.addToList(
user_id,
list_id,
stock_ticker
).then((lists) => dispatch(receiveLists(lists)));
};
export const addToAllLists = (
user_id,
list_ids,
stock_ticker
) => (dispatch) => {
dispatch(startListLoading());
//return a single promise using Promise.all
return Promise.all(
list_ids.map((list_id) =>
//also add (dispatch) so you actually call the thunk
addToList(user_id, list_id, stock_ticker)(dispatch)
)
).then(()=>dispatch(stopListLoading()));
};
There was a syntax error in the last line, should have been .then(()=>dispatch(stopListLoading())); looking at your parameter names I can see you are not used to write JS code as it's easy to spot if you run it, below is a working example:
const { Provider, useDispatch } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
//actions
const later = (...args) =>
new Promise((r) => setTimeout(() => r(args), 100));
const StockApiutil = {
addToList: (a, b, c) => later(a, b, c),
};
const receiveLists = (list) => ({
type: 'recieveList',
payload: list,
});
const startListLoading = (payload) => ({
type: 'startListLoading',
payload,
});
const stopListLoading = (payload) => ({
type: 'stopListLoading',
payload,
});
const addToList = (user_id, list_id, stock_ticker) => (
dispatch
) => {
return StockApiutil.addToList(
user_id,
list_id,
stock_ticker
).then((lists) => dispatch(receiveLists(lists)));
};
const addToAllLists = (user_id, list_ids, stock_ticker) => (
dispatch
) => {
dispatch(startListLoading());
//return a single promise using Promise.all
return Promise.all(
list_ids.map((list_id) =>
//also add (dispatch) so you actually call the thunk
addToList(user_id, list_id, stock_ticker)(dispatch)
)
).then(() => dispatch(stopListLoading()));
};
const reducer = (state, { type, payload }) => {
console.log('in reducer:', type, payload);
return state;
};
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
{},
composeEnhancers(
applyMiddleware(
({ dispatch, getState }) => (next) => (action) =>
//simple thunk implementation
typeof action === 'function'
? action(dispatch, getState)
: next(action)
)
)
);
const App = () => {
const dispatch = useDispatch();
React.useEffect(
() =>
dispatch(
addToAllLists(
'user id',
[1, 2, 3, 4, 5],
'stock ticker'
)
),
[dispatch]
);
return 'check the console';
};
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>
I'm using a workbox and trying to cache requests something like this:
this.serviceWorker.addEventListener('fetch', (event) => {
const request = event.request;
event.respondWith(
caches.open('cache name')
.then(cache => cache.match(request))
.then(async (cachedResponse) => {
// todo something
How Can I cancel event.request and send my custom request?
Thanks for any helping.
You may alter or completely change the out-going request like so:
this.serviceWorker.addEventListener('fetch', (event) => {
const request = event.request;
return event.respondWith(
caches.open('cache name')
.then(cache => cache.match(request))
.then(async (cachedResponse) => {
// create a new request, fetchi it, and return the result
return fetch(new Request(request.url ....))
})
Pay attention to the return statement I added to the third line of code :)
More info about constructing the new request: https://stackoverflow.com/a/35421858/5038943
I want to develop a logout button that will send me to the log in route and remove all other routes from the Navigator. The documentation doesn't seem to explain how to make a RoutePredicate or have any sort of removeAll function.
I was able to accomplish this with the following code:
Navigator.of(context)
.pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
The secret here is using a RoutePredicate that always returns false (Route<dynamic> route) => false. In this situation it removes all of the routes except for the new /login route I pushed.
i can done with the following code snippet :
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) =>
LoginScreen()), (Route<dynamic> route) => false);
if you want to remove all the route below the pushed route, RoutePredicate always return false, e.g (Route route) => false.
Another alternative is popUntil()
Navigator.of(context).popUntil(ModalRoute.withName('/root'));
This will pop all routes off until you are back at the named route.
Another solution is to use pushAndRemoveUntil(). To remove all other routes use ModalRoute.withName('/')
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (BuildContext context) => Login()),
ModalRoute.withName('/')
);
Reference: https://api.flutter.dev/flutter/widgets/NavigatorState/pushAndRemoveUntil.html
In case you want to go back to the particular screen and you don't use named router can use the next approach
Example:
Navigator.pushAndRemoveUntil(context,
MaterialPageRoute(builder: (BuildContext context) => SingleShowPage()),
(Route<dynamic> route) => route is HomePage
);
With route is HomePage you check the name of your widget.
If you are using namedRoutes, you can do this by simply :
Navigator.pushNamedAndRemoveUntil(context, "/login", (Route<dynamic> route) => false);
Where "/login" is the route you want to push on the route stack.
Note That :
This statement removes all the routes in the stack and makes the pushed one the root.
use popUntil like following
Navigator.popUntil(context, (route) => route.isFirst);
I don't know why no one mentioned the solution using SchedularBindingInstance, A little late to the party though, I think this would be the right way to do it originally answered here
SchedulerBinding.instance.addPostFrameCallback((_) async {
Navigator.of(context).pushNamedAndRemoveUntil(
'/login',
(Route<dynamic> route) => false);
});
The above code removes all the routes and naviagtes to '/login' this also make sures that all the frames are rendered before navigating to new route by scheduling a callback
Not sure if I'm doing this right
but this suits my use-case of popping until by root widget
void popUntilRoot({Object result}) {
if (Navigator.of(context).canPop()) {
pop();
popUntilRoot();
}
}
In my case this solution works:
Navigator.pushNamedAndRemoveUntil(" The Route you want to go to " , (Route route) => false);
In my case I had this painting
Page 1 (Main) -> Page 2 -> Page 3 -> Page 4.
When I had to go to Page 4, the Page 2 and Page 3 going back did not have to appear, but I had to go to Page 1 again. At this point going to Page 4 I did:
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) =>
Workout()),
(Route<dynamic> route) => route.isFirst);
The instructions are: go to page 4 (Workout) and remove all previous pages up to 1, that is (Main).
In your case that can be to switch from anything to a Login, then:
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) =>
Login()),
(Route<dynamic> route) => false);
That is, go to Login and remove all previous pages, because there is a false.
This is working for me. Actually, I was working with bloc but my issue was login screen bloc. It was not updating after logout. It was holding the previous model data. Even, I entered the wrong entry It was going to Home Screen.
Step 1:
Navigator.of(context).pushNamedAndRemoveUntil(
UIData.initialRoute, (Route<dynamic> route) => false);
where,
UIData.initialRoute = "/" or "/login"
Step 2:
It's working to refresh the screen. If you are working with Bloc then It will very helpful.
runApp(MyApp());
where,
MyApp() is the root class.
Root class (i.e. MyApp) code
class MyApp extends StatelessWidget {
final materialApp = Provider(
child: MaterialApp(
title: UIData.appName,
theme: ThemeData(accentColor: UIColor().getAppbarColor(),
fontFamily: UIData.quickFont,
),
debugShowCheckedModeBanner: false,
//home: SplashScreen(),
initialRoute: UIData.initialRoute,
routes: {
UIData.initialRoute: (context) => SplashScreen(),
UIData.loginRoute: (context) => LoginScreen(),
UIData.homeRoute: (context) => HomeScreen(),
},
onUnknownRoute: (RouteSettings rs) => new MaterialPageRoute(
builder: (context) => new NotFoundPage(
appTitle: UIData.coming_soon,
icon: FontAwesomeIcons.solidSmile,
title: UIData.coming_soon,
message: "Under Development",
iconColor: Colors.green,
)
)));
#override
Widget build(BuildContext context) {
return materialApp;
}
}
void main() => runApp(MyApp());
Here is My Logout method,
void logout() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.clear();
// TODO: we can use UIData.loginRoute instead of UIData.initialRoute
Navigator.of(context).pushNamedAndRemoveUntil(
UIData.initialRoute, (Route<dynamic> route) => false);
//TODO: It's working as refresh the screen
runApp(MyApp());
}
Use this, it worked perfectly for me:
Navigator.pushNamedAndRemoveUntil(
context, '/loginscreen', (Route<dynamic> route) => false);
Make sure you add the last line, parameter, and you're good to go.
First see chrislondon answer, and then know that you can also do this, if you do not have access to the (context).
navigatorKey.currentState.pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
to clear route -
onTap: () {
//todo to clear route -
Navigator.of(context).pop();
Navigator.push(context, MaterialPageRoute(builder: (context) => UpdateEmployeeUpdateDateActivity(_token),));
widget.listener.onEmployeeDateClick(_day,_month, _year);
}