I am building a demo app to test using localization strings.
I get the following error:
I/flutter (21588): The following NoSuchMethodError was thrown building MainApp(dirty):
I/flutter (21588): The getter 'title' was called on null.
I/flutter (21588): Receiver: null
I/flutter (21588): Tried calling: title
I am not sure why I am getting this error. I have followed the indications on flutter documentation.
I have following Localization Class:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'dart:async';
import 'package:bet_master/l10n/messages_all.dart';
class AppLocalizations {
static Future<AppLocalizations> load(Locale locale) {
final String name =
locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((bool _) {
Intl.defaultLocale = localeName;
return AppLocalizations();
});
}
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
String get title {
return Intl.message(
'Bet Master',
name: 'title',
desc: 'App Title'
);
}
String get search {
return Intl.message(
'Search',
name: 'search',
desc : ''
);
}
}
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const AppLocalizationsDelegate();
#override
bool isSupported(Locale locale) {
return ['en', 'es', 'fr'].contains(locale.languageCode);
}
#override
Future<AppLocalizations> load(Locale locale) {
return AppLocalizations.load(locale);
}
#override
bool shouldReload(AppLocalizationsDelegate old) {
return false;
}
}
For the Home Widget I am only setting the title
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:bet_master/localization/localizations.dart';
void main() {
runApp(MainApp());
}
class MainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
localizationsDelegates: [
const AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: [
const Locale('en', ''),
const Locale('es', ''),
const Locale('fr', ''),
],
home: Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).title),
),
),
);
}
}
At the end the issue seems related to Localization not being available, I moved the "home" code to another widget and solved the issue.
Widget build(BuildContext context) {
return new MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
_appLocalizationsDelegate,
],
supportedLocales: [
Locale('en'),
Locale('es'),
],
locale: _appLocalizationsDelegate.overridenLocale,
onGenerateTitle: (BuildContext context) => AppLocalizations.of(context).title,
home: Home()
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).title),
),
body: Column(children: <Widget>[
Text('hola'),
],)
);
}
}
I need to research still why is this needed, but at least it works now.
I had the same error, but my problem was that I was using two nested
MaterialApp() widgets.
It was causing that this line retuns null:
return Localizations.of<AppLocalizations>(context, AppLocalizations);
I replaced the nested MarerialApp(The child) widget with a Scaffold and It worked.
Hope It helps someone!
Related
i am making an app that contains a button which when pressed will open a website that streams video . i have used flutter inappwebview plugin and i want to use content blockers too in my code.after searching i got some codes but i am getting errors that says some part in my code isnt define.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
final Uri _url = Uri.parse('https://zoro.to');
Future<void> _launchUrl() async {
if (!await launchUrl(_url)) {
throw Exception('Could not launch $_url');
}
}
// Future main() async {
// WidgetsFlutterBinding.ensureInitialized();
//
// if (Platform.isAndroid) {
// await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
// }
//
// runApp(new animflix());
// }
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kIsWeb &&
kDebugMode &&
defaultTargetPlatform == TargetPlatform.android) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(
kDebugMode);
}
runApp(const MaterialApp(home: animflix()));
}
// void main() {
// runApp(const animflix());
// }
class animflix extends StatelessWidget {
const animflix({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'anime',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: anime(),
);
}
}
class anime extends StatefulWidget {
const anime({Key? key}) : super(key: key);
#override
State<anime> createState() => _animeState();
}
class _animeState extends State<anime> {
final GlobalKey webViewKey = GlobalKey();
final adUrlFilters = [
".*.doubleclick.net/.*",
".*.ads.pubmatic.com/.*",
".*.googlesyndication.com/.*",
".*.google-analytics.com/.*",
".*.adservice.google.*/.*",
".*.adbrite.com/.*",
".*.exponential.com/.*",
".*.quantserve.com/.*",
".*.scorecardresearch.com/.*",
".*.zedo.com/.*",
".*.adsafeprotected.com/.*",
".*.teads.tv/.*",
".*.outbrain.com/.*"
];
final List<ContentBlocker> contentBlockers = [];
var contentBlockerEnabled = true;
InAppWebViewController? webViewController;
#override
void initState() {
super.initState();
// for each ad URL filter, add a Content Blocker to block its loading
for (final adUrlFilter in adUrlFilters) {
contentBlockers.add(ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: adUrlFilter,
),
action: ContentBlockerAction(
type: ContentBlockerActionType.BLOCK,
)));
}
// apply the "display: none" style to some HTML elements
contentBlockers.add(ContentBlocker(
trigger: ContentBlockerTrigger(
urlFilter: ".*",
),
action: ContentBlockerAction(
type: ContentBlockerActionType.CSS_DISPLAY_NONE,
selector: ".banner, .banners, .ads, .ad, .advert")));
}
adblock() async {
contentBlockerEnabled = !contentBlockerEnabled;
if (contentBlockerEnabled) {
await webViewController?.setSettings(
settings: InAppWebViewSettings(contentBlockers: contentBlockers));
} else {
await webViewController?.setSettings(
settings: InAppWebViewSettings(contentBlockers: []));
}
webViewController?.reload();
setState(() {});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.pink,
),
home: Scaffold(
appBar: AppBar(title: Text("Anime market")),
body: Center(
child: Column(
children: <Widget>[
Container(
child: ElevatedButton(
onPressed: () {
setState(() {
InAppWebView(
key: webViewKey,
initialUrlRequest: URLRequest(
url: Uri.parse('https://www.tomshardware.com/')),
initialData: InAppWebViewSettings(
contentBlockers: contentBlockers),
onWebViewCreated: (controller) {
webViewController = controller;
},
);
});
},
child: Text("sflix"),
),
)
],
)),
),
);
}
}
"InAppWebviewSettings" throughs an error which says it isnt defined.
I have some FutureBuilder that returns a DrobdownButton filled with the result of a HTTP request, using JSON response. I get the DropdownButton filled and everything seems to work just fine. The problem is after selecting an element I got an error:
'package:flutter/src/material/dropdown.dart': Failed assertion: line 609 pos 15: 'items == null ||
I/flutter ( 5210): items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value ==
I/flutter ( 5210): value).length == 1': is not true.
I understand that, for some reason, the DropdownButton get empty after selection or so...
I tried this code:
https://inducesmile.com/google-flutter/how-to-populate-dropdownbutton-using-json-api-in-flutter/
But in this case, after selection, the selected value goes to another widget. The thing I want is to use de value: parameter of the DropdownButton.
In the code above, the value: parameter is commented. I tried this code with no comment on that line and remove the SizedBox. The only thing I want is that the value is selected on the same DropdownButton
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Items _currentItem;
Future<List<Items>> _getItemsData() async {
List<Items> dataItems = new List<Items>();
for (int i = 1; i <= 3; i++) {
dataItems.add(
Items.fromJson(
{'id':i,'name':'Elem ${i}'}
)
);
}
sleep(new Duration(seconds: 5));
return dataItems;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new FutureBuilder(
future: _getItemsData(),
builder: (BuildContext context, AsyncSnapshot<List<Items>> snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
} else {
return new Container(
width: 280,
child: new DropdownButton<Items>(
items: snapshot.data.map((items) => DropdownMenuItem<Items>(
child: Text(items.name),
value: items,
)).toList(),
onChanged: (Items value) {
setState(() {
_currentItem = value;
});
},
isExpanded: true,
value: _currentItem,
hint: Text('Select one...')
)
);
}
}
)
]
)
)
);
}
}
class Items {
int id;
String name;
Items({this.id, this.name});
factory Items.fromJson(Map<String, dynamic> json) {
return Items (
id: json['id'],
name: json['name']
);
}
}
As an outsider looking into flutter state management, I liked the idea of using scoped_model for state management as recommended in flutter.dev. I have a rewrite of the counter app running. I am able to access the model properties using ScopedModelDescendant<CounterModel>, but I am having troubles accessing the model properties using ScopedModel.of<CounterModel>(context). Could someone please advice what I might be doing wrong? I have a hunch that it could be where the ScopedModel is in my widget tree. My code and error message follows.
main.dart
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:second/model/counter_model.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(context) {
return ScopedModel(
model: new CounterModel(),
child: Scaffold(
appBar: AppBar(
title: Text('ScopedModel'),
),
body: ScopedModelDescendant<CounterModel>(
builder: (context, child, value) {
return Text("Pressed ${value.counter} times");
},
),
floatingActionButton: buildFab1()),
);
}
Widget buildFab1() {
return ScopedModelDescendant<CounterModel>(
builder: (context, child, model) => FloatingActionButton(
child: Icon(Icons.add),
onPressed: model.incrementCounter,
),
);
}
Widget buildFab2(BuildContext context) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: ScopedModel.of<CounterModel>(context).incrementCounter,
);
}
}
model/counter_model.dart
import 'package:scoped_model/scoped_model.dart';
class CounterModel extends Model {
int _counter = 0;
int get counter => _counter;
void incrementCounter() {
_counter++;
notifyListeners();
}
}
In main.dart, if I use buildFab2(context) instead of buildFab1(), I get the following error
flutter: The following ScopedModelError was thrown building ScopedModelDescendant<Model>(dirty):
flutter: Error: Could not find the correct ScopedModel.
flutter:
flutter: To fix, please:
flutter:
flutter: * Provide types to ScopedModel<MyModel>
flutter: * Provide types to ScopedModelDescendant<MyModel>
flutter: * Provide types to ScopedModel.of<MyModel>()
flutter: * Always use package imports. Ex: `import 'package:my_app/my_model.dart';
flutter:
I took a look at a few SO questions, but none helped.
Yes, that because, the context you pass will not have a ScopedModel of CounterModel.
What you can do is wrap your buildFab2 inside a Builder widget which will provide you with a context having ScopedModel with CounterModel as parent.
Like:
Builder(
builder: (context){
return buildFab2(context);
},
)
You app will look like:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(context) {
return ScopedModel(
model: new CounterModel(),
child: Scaffold(
appBar: AppBar(
title: Text('ScopedModel'),
),
body: ScopedModelDescendant<CounterModel>(
builder: (context, child, value) {
return Text("Pressed ${value.counter} times");
},
),
floatingActionButton: Builder(
builder: (context) {
// return buildFab1() if fab one required
return buildFab2(context);
},
),
),
);
}
Widget buildFab1() {
return ScopedModelDescendant<CounterModel>(
builder: (context, child, model) => FloatingActionButton(
child: Icon(Icons.add),
onPressed: model.incrementCounter,
),
);
}
Widget buildFab2(BuildContext context) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: ScopedModel.of<CounterModel>(context).incrementCounter,
);
}
}
Hope that helps!
You need to add the scoppedmodel in the main.dart like this:
#override
Widget build(BuildContext context){
return ScopedModel<UserModel>(
model: UserModel(),
child: MaterialApp(
title: "Quiz Flamengo",
debugShowCheckedModeBanner: false,
home: Scaffold(
body: HomeScreen(),
)
)
);
}
By implementation of FlutterWebviewPlugin, I want to show a particular website in a widget but without header and footer.
is this possible in Flutter?
I guess there is a function in FlutterWebviewPlugin class .evalJavascript('some code') but don't know how to use this function. can I add javascript code to this?
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
String url = "https://flutter.io/";
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Webview Example',
theme: ThemeData.dark(),
routes: {
"/": (_) => Home(),
"/webview": (_) => WebviewScaffold(
url: url,
withJavascript: true,
withLocalStorage: true,
withZoom: true,
)
},
);
}
}
class Home extends StatefulWidget {
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
final webView = FlutterWebviewPlugin();
TextEditingController controller = TextEditingController(text: url);
#override
void initState() {
super.initState();
webView.close();
controller.addListener(() {
url = controller.text;
});
}
#override
void dispose() {
webView.dispose();
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("WebView"),
),
body: Center(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10.0),
child: TextField(
controller: controller,
),
),
RaisedButton(
child: Text("Open Webview"),
onPressed: () {
Navigator.of(context).pushNamed("/webview");
},
)
],
),
)
);
}
}
I suggest using Flutter's official WebView plugin: webview_flutter
The plugin also has a method that can run Javascript using WebViewController.evaluateJavascript(String). This method is recommended to be run after WebView.onPageFinished callback.
Your WebView widget should look like this.
WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_webViewController = webViewController;
_controller.complete(webViewController);
},
onProgress: (int progress) {
print("WebView is loading (progress : $progress%)");
},
onPageStarted: (String url) {
print('Page started loading: $url');
},
onPageFinished: (String url) {
print('Page finished loading: $url');
// Removes header and footer from page
_webViewController
.evaluateJavascript("javascript:(function() { " +
"var head = document.getElementsByTagName('header')[0];" +
"head.parentNode.removeChild(head);" +
"var footer = document.getElementsByTagName('footer')[0];" +
"footer.parentNode.removeChild(footer);" +
"})()")
.then((value) => debugPrint('Page finished loading Javascript'))
.catchError((onError) => debugPrint('$onError'));
},
);
Here's a complete sample that you can try.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
WebViewController _webViewController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_webViewController = webViewController;
_controller.complete(webViewController);
},
onProgress: (int progress) {
print("WebView is loading (progress : $progress%)");
},
javascriptChannels: <JavascriptChannel>{
_toasterJavascriptChannel(context),
},
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
onPageStarted: (String url) {
print('Page started loading: $url');
},
onPageFinished: (String url) {
print('Page finished loading: $url');
_webViewController
.evaluateJavascript("javascript:(function() { " +
"var head = document.getElementsByTagName('header')[0];" +
"head.parentNode.removeChild(head);" +
"var footer = document.getElementsByTagName('footer')[0];" +
"footer.parentNode.removeChild(footer);" +
"})()")
.then((value) => debugPrint('Page finished loading Javascript'))
.catchError((onError) => debugPrint('$onError'));
},
gestureNavigationEnabled: true,
);
}),
);
}
JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
});
}
}
How the app looks running
_webViewController.runJavascript(
"document.getElementsByTagName('header')[0].style.display='none'");
_webViewController.runJavascript(
"document.getElementsByTagName('footer')[0].style.display='none'");
You can use the flutter_inappwebview plugin (I'm the author) and inject an UserScript at UserScriptInjectionTime.AT_DOCUMENT_START to hide or remove HTML elements when the web page loads (check JavaScript - User Scripts official docs for User Scripts details).
As I have already answered here for a similar issue, here is a code example using the current latest version 6 (6.0.0-beta.18) with URL https://getmobie.de/impressum that removes the header and footer HTML elements:
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kIsWeb &&
kDebugMode &&
defaultTargetPlatform == TargetPlatform.android) {
await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode);
}
runApp(const MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey webViewKey = GlobalKey();
InAppWebViewController? webViewController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("InAppWebView test"),
),
body: Column(children: <Widget>[
Expanded(
child: InAppWebView(
key: webViewKey,
initialUrlRequest:
URLRequest(url: WebUri("https://getmobie.de/impressum")),
initialUserScripts: UnmodifiableListView([
UserScript(source: """
window.addEventListener('DOMContentLoaded', function(event) {
var header = document.querySelector('.elementor-location-header'); // use here the correct CSS selector for your use case
if (header != null) {
header.remove(); // remove the HTML element. Instead, to simply hide the HTML element, use header.style.display = 'none';
}
var footer = document.querySelector('.elementor-location-footer'); // use here the correct CSS selector for your use case
if (footer != null) {
footer.remove(); // remove the HTML element. Instead, to simply hide the HTML element, use footer.style.display = 'none';
}
});
""", injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START)
]),
onWebViewCreated: (controller) {
webViewController = controller;
},
),
),
]));
}
}
For your use case, use the right CSS selector inside the user script js source to correctly get and remove the header and footer HTML elements from your web page!
I have many screens, and I'm using the Navigator. I'd like to use "named routes", but I also need to pass non-string (such as images) to my next route.
I can't use pushNamed() because I can't pass non-string data to it.
How can I use a named route + send non-string data?
EDIT:
It is now possible to pass complex arguments to Navigator.pushNamed:
String id;
Navigator.pushNamed(context, '/users', arguments: id);
It can then be used within onGenerateRoute to customize route building with these arguments:
MaterialApp(
title: 'Flutter Hooks Gallery',
onGenerateRoute: (settings) {
final arguments = settings.arguments;
switch (settings.name) {
case '/users':
if (arguments is String) {
// the details page for one specific user
return UserDetails(arguments);
}
else {
// a route showing the list of all users
return UserList();
}
default:
return null;
}
},
);
You can use the parameter routes of your App for directly passing arguments.
Like this:
routes: {
HomePage.route: (_) => HomePage(),
DetailsPage.route: (context) =>
DetailsPage(ModalRoute.of(context).settings.arguments),
},
In this case, the complete example will look like the next:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
initialRoute: HomePage.route,
routes: {
HomePage.route: (_) => HomePage(),
DetailsPage.route: (context) =>
DetailsPage(ModalRoute.of(context).settings.arguments),
},
);
}
}
class HomePage extends StatelessWidget {
static const String route = '/';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.pushNamed(context, '/details',
arguments: ScreenArguments(
'My Details',
'Some Message',
));
},
),
);
}
}
class DetailsPage extends StatelessWidget {
static const String route = '/details';
final ScreenArguments arguments;
DetailsPage(this.arguments);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(arguments.title),
),
body: Center(
child: Text(arguments.message),
),
);
}
}
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}
By Using Maps
While pushing the arguments u can push that in map form and can do the same while extracting them.
e.g.
While Pushing
Navigator.of(context).pushNamed(
'second',
arguments: {
'title':'This is a String',
or
'Fx': This could be any widget or Function
}
While Extracting the arguments in the target page
final routes=ModalRoute.of(context).settings.arguments as Map<String,String>;
return Scaffold(
appBar: AppBar(
title: Text(routes['title']),
),
body: Container(
child: Center(
child: RaisedButton(
child: Text("Back"),
onPressed: ()=>Navigator.of(context).pop(),
),
),
),
);
and choose your map accordingly accordingly
UPDATE: 3rd April, 2021
This answer is old and Flutter navigation has evolved considerably since then. This may not be the best way to handle navigation with current versions, please consider other answers. I will leave this here for historical purposes.
Using onGenerateRoute it is easy to pass complex arguments on route transition with Navigator.pushNamed or Navigator.pushReplacementNamed
A minimal setup to show the concept would be
main.dart
import 'package:flutter/material.dart';
import 'package:navigator/routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Navigation Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) => makeRoute(
context: context,
routeName: settings.name,
arguments: settings.arguments,
),
maintainState: true,
fullscreenDialog: false,
);
},
);
}
}
routes.dart
In the _buildRoute method we check the route name and cast arguments to a required type.
A draw back is that the type has to be defined before hand if required argument is not a simple type.
import 'package:flutter/material.dart';
import 'package:navigator/list.dart';
import 'package:navigator/details.dart';
Widget makeRoute(
{#required BuildContext context,
#required String routeName,
Object arguments}) {
final Widget child =
_buildRoute(context: context, routeName: routeName, arguments: arguments);
return child;
}
Widget _buildRoute({
#required BuildContext context,
#required String routeName,
Object arguments,
}) {
switch (routeName) {
case '/':
return ArticleList();
case '/ArticleView':
Article article = arguments as Article;
return ArticleView(article: article);
default:
throw 'Route $routeName is not defined';
}
}
Views
list.dart
Construct the route argument using a defined type, Article in our case.
import 'package:flutter/material.dart';
import 'package:navigator/details.dart' show Article;
class ArticleList extends StatefulWidget {
#override
_ArticleListState createState() => _ArticleListState();
}
class _ArticleListState extends State<ArticleList> {
List<Article> articles = [
Article(
id: 1,
title: 'Article 1',
author_name: 'Nilotpal',
summary: 'Article 1 summary'),
Article(
id: 2,
title: 'Article 2',
author_name: 'Mike',
summary: 'Article 2 summary'),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Articles'),
),
body: Center(
child: Column(
children: <Widget>[
ListTile(
title: Text('${articles[0].title}'),
subtitle: Text('by ${articles[0].author_name}'),
onTap: () {
Navigator.of(context)
.pushNamed('/ArticleView', arguments: articles[0]);
},
),
ListTile(
title: Text('${articles[1].title}'),
subtitle: Text('by ${articles[1].author_name}'),
onTap: () {
Navigator.of(context)
.pushNamed('/ArticleView', arguments: articles[1]);
},
),
],
),
),
);
}
}
details.dart
Define a type for the arguments
import 'package:flutter/material.dart';
class Article {
final int id;
final String author_name;
final String title;
final String summary;
Article(
{#required this.id,
#required this.author_name,
#required this.title,
#required this.summary});
}
class ArticleView extends StatelessWidget {
final Article _article;
ArticleView({#required Article article}) : _article = article;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${_article.title}'),
),
body: SafeArea(
top: true,
child: Center(
child: Column(
children: <Widget>[
Text('${_article.author_name}'),
Text('${_article.summary}'),
],
),
),
),
);
}
}
The Flutter Cookbook shows how to navigate to a new page and pass non-string data to it.
Passing data to next page
I started with Navigator.pushedNamed() because it was simple and I didn't have any data to pass. When my needs changed and I wanted to pass data, I switched to Navigator.push().
Example:
var nextPageData = {foo:'bar'};
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
MyNextPage(myData: nextPageData))
);
I am capturing images with camera then passing them through to a confirmation page like so:
ImagePicker.pickImage(source: source).then((File file) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MediaCaptured(file: file),
));
});
You could easily do the same with any type of file or non-string data.
var foo = "non-string data";
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MediaCaptured(foo: foo),
));
Call the next page in the route by it's class name, as above.
Just make sure your new page accepts this in it's constructor.
// Stateful Widget
class MediaCaptured extends StatefulWidget {
MediaCaptured({ Key key, #required this.foo,}) : super(key: key);
final var foo;
}
// StatelessWidget
class MediaCaptured extends StatelessWidget {
MediaCaptured(this.foo);
var foo;
}
For the outcome of this problem, I developed the package
link: https://pub.dartlang.org/packages/navigate
That provide to much your expect and easy to use
Navigate.navigate(context,
"home",
transactionType:TransactionType.fromLeft , // optional
replaceRoute: ReplaceRoute.thisOne, //optional
arg: {"transactionType":TransactionType.fromLeft,"replaceRoute":ReplaceRoute.thisOne} //optional
);
From First StateFul class :
Navigator.of(context).pushNamed('/pending_order',arguments: {"staff" : staffObj});
To Second StateFul class :
class PendingOrders extends StatefulWidget {
#override
_PendingOrdersState createState() => _PendingOrdersState();
}
class _PendingOrdersState extends State<PendingOrders> {
StaffModel staffModelObj;
#override
Widget build(BuildContext context) {
final routes =
ModalRoute.of(context).settings.arguments as Map<String, dynamic>;
if (routes != null) {
staffModelObj = routes["staff"];
}
return Scaffold(...);}}
I wanted to use a named route navigator that has values as below
Navigator.pushNamed(context, '/increaseBalanceAccountPage',
arguments: {'accountBalanceViewModel': result},);
so I should define that route in materialApp widget in the start of app but I should give parameters in the instance so I solve my problem with some modification of #YuriyLuchaninov code Like below:
MaterialApp(
initialRoute: "/",
routes: {
'/': (context) => SplashScreenPage(),
"/increaseBalanceAccountPage":
(context) =>
UserAccountBalancePage(accountBalanceViewModel: Map<String,Object>
.from(ModalRoute.of(context)!.settings.arguments as Map).values.first as
AccountBalanceViewModel)
},
.....
Consider this trivial example from flutter. You have a class created as follows
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}
Now we will pass an object of this class as an argument as follows
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: ScreenArguments(
'Extract Arguments Screen',
'This message is extracted in the build method.',
),
);
And then you can extract the arguments as follows
final args = ModalRoute.of(context)!.settings.arguments as ScreenArguments;
And that is all. Hope this helps Source: passing arguments to a named route
We can pass any type of arguments when declaring routes as constructor arguments as below,
For example to send a list of Strings,
List<String> titles = [];
void main() => runApp(
new MaterialApp(
home: new FirstPage(),
routes: <String, WidgetBuilder>{
"/SecondPage": (BuildContext context) => new SecondPage(titles),
},
),
);
class FirstPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Container(
child: new RaisedButton(onPressed: () {
Navigator.of(context).pushNamed('/SecondPage');
}),
);
}
}
class SecondPage extends StatelessWidget {
final List<String> titles;
SecondPage(this.titles);
#override
Widget build(BuildContext context) {
return new ListView.builder(
itemBuilder: (context, index) {
return new ListTile(
title: new Text(titles[index]),
);
},
);
}
}