Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体


10 questions online user: 25

3
votes
answers
31 views
+10

Flutter - How to set status bar color when AppBar not present

How to set status bar color when AppBar not present.

I have tried this but not working.

Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
    return new Scaffold(
        body: new Container(
        color: UniQueryColors.colorBackground,
        child: new ListView.builder(
           itemCount: 7,
           itemBuilder: (BuildContext context, int index){
             if (index == 0){
               return addTopInfoSection();
             }
           },
        ),
       ),
    );
}

Output look like this:

enter image description here

沙发
+20
Widget build(BuildContext context) {

    return new Scaffold(
        body: new Container(
            color: UniQueryColors.colorBackground,

    /* Wrapping ListView.builder with MediaQuery.removePadding() removes the default padding of the ListView.builder() and the status bar takes the color of the app background */

        child: MediaQuery.removePadding(
         removeTop: true,
         context: context,
        child: ListView.builder(
           itemCount: 7,
           itemBuilder: (BuildContext context, int index){
             if (index == 0){
               return addTopInfoSection();
             }
           },
        ),
       ),
      ),
    );
}
板凳
+10

The status bar colour is rendered by the Android system. Whether that can be set from Flutter or not is up for debate: How to make Android status bar light in Flutter

What you can do however, is change the status bar colour in the Android specific code by editing the theme: How to change the status bar color in android

For iOS you'll have to see their documentation - I'm not familiar with the platform.

There are in fact two Dart libraries, one for setting the light/dark theme of the statusbar and the other for setting the colour. I haven't used either, but clearly someone else has had the same issue you're facing and ended up developing their own package.

謝謝你的建議。我會檢查他們。 - Rafiqul Hasan 18年5月24日6:38

地板
0

As the solution is already mentioned, I am implementing it in a different approach. The approach followed is removing AppBar and changing the color of the status bar using Container widget.

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Test',
      home: Scaffold(
        primary: true,
        appBar: EmptyAppBar(),
        body: MyScaffold(),
      ),
    ),
  );
}

class MyScaffold extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(
        'Test',
      ),
    );
  }
}

class EmptyAppBar extends StatelessWidget implements PreferredSizeWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black,
    );
  }

  @override
  Size get preferredSize => Size(0.0, 0.0);
}
  • Here I am using EmptyAppBar class for removing the AppBar which is by default present in Scaffold
  • In EmptyAppBar class we can choose the required color in the container widget.
  • After that, you have your own custom MyScaffold class for creating your widgets. In my code, I've created a text.

Reference: GitHub Issue

這個問題已經解決了:) - 藍色phoenox 18年5月3日在20:32

這為我解決了類似的問題。我不知道Flutter是什麼,但我的Android Studio保持重新索引並變得毫無用處。 - 格雷格18年8月15日19:26

4楼
0

you should solve this question in two ways:

  1. you did not set appBar then you just write in this way:
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark.copyWith(
  statusBarColor: Colors.black, 
));

or

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light.copyWith(
  statusBarColor: Colors.white, 
));
  1. you always set appBar so you should set appBar but not upon code:
Scaffold(
  appBar: AppBar(
    brightness: Brightness.light,
  )
)

or

Scaffold(
  appBar: AppBar(
    brightness: Brightness.dark,
  )
)

作品!在我的情況下,.iml文件看起來像這樣: - Thorben 18年5月12日13:50

我已經有這條線,仍然索引... android工作室是我曾經下載的最狡猾的IDE - Rafael Lima 18年5月26日在0:08

5楼
0

Use EmptyAppBar, with some code for restoring color as in AppBar.

class EmptyAppBar  extends StatelessWidget implements PreferredSizeWidget {
  static const double _defaultElevation = 4.0;
  @override
  Widget build(BuildContext context) {
    final ThemeData themeData = Theme.of(context);
    final AppBarTheme appBarTheme = AppBarTheme.of(context);
    final Brightness brightness = appBarTheme.brightness
        ?? themeData.primaryColorBrightness;
    final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
        ? SystemUiOverlayStyle.light
        : SystemUiOverlayStyle.dark;

    return Semantics(
      container: true,
      child: AnnotatedRegion<SystemUiOverlayStyle>(
        value: overlayStyle,
        child: Material(
          color: appBarTheme.color
              ?? themeData.primaryColor,
          elevation: appBarTheme.elevation
              ?? _defaultElevation,
          child: Semantics(
            explicitChildNodes: true,
            child: Container(),
          ),
        ),
      ),
    );
  }
  @override
  Size get preferredSize => Size(0.0,0.0);
}

感謝您提供這些。我能夠在您提供的其中一個鏈接中找到修復程序。我在這裡的答案中添加了詳細信息。 - SeaFuzz 18年4月2日18:23

6楼
0

You can use SystemChrome class to change Status bar and Navigation bar color. First import

import 'package:flutter/services.dart';

After this, you need to add following lines (better place to put these lines is in your main() method)

void main() {
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    systemNavigationBarColor: Colors.blue, // navigation bar color
    statusBarColor: Colors.pink, // status bar color
  ));
}

直到什麼時候你會重複這個過程。我幾乎在所有項目中都面臨這個問題。要刪除它們,重新克隆並重新加載它們??? - Mohammedsalim Shivani於18年5月5日8:02

7楼
-10
Here You can use flutter flutter_statusbar_manager 1.0.2 lib


Flutter Statusbar Manager, lets you control the status bar color, style (theme), visibility, and translucent properties across iOS and Android. With some added bonus for Android to control the Navigation Bar.


import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_statusbar_manager/flutter_statusbar_manager.dart';

void main() => runApp(new MyApp());

class MyApp extends StatefulWidget {
  MyApp();

  factory MyApp.forDesignTime() {
    // TODO: add arguments
    return new MyApp();
  }

  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double _statusBarHeight = 0.0;
  bool _statusBarColorAnimated = false;
  Color _statusBarColor = Colors.black;
  double _statusBarOpacity = 1.0;
  bool _statusBarHidden = false;
  StatusBarAnimation _statusBarAnimation = StatusBarAnimation.NONE;
  StatusBarStyle _statusBarStyle = StatusBarStyle.DEFAULT;
  bool _statusBarTranslucent = false;
  bool _loadingIndicator = false;
  bool _fullscreenMode = false;

  bool _navBarColorAnimated = false;
  Color _navBarColor = Colors.black;
  NavigationBarStyle _navBarStyle = NavigationBarStyle.DEFAULT;

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    double statusBarHeight;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      statusBarHeight = await FlutterStatusbarManager.getHeight;
    } on PlatformException {
      statusBarHeight = 0.0;
    }
    if (!mounted) return;

    setState(() {
      _statusBarHeight = statusBarHeight;
    });
  }

  Widget renderTitle(String text) {
    final textStyle = TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold);
    return Text(text, style: textStyle);
  }

  void colorBarChanged(Color val) {
    this.setState(() {
      _statusBarColor = val;
    });
    updateStatusBar();
  }

  void updateStatusBar() {
    FlutterStatusbarManager.setColor(
        _statusBarColor.withOpacity(_statusBarOpacity),
        animated: _statusBarColorAnimated);
  }

  void statusBarAnimationChanged(StatusBarAnimation val) {
    this.setState(() {
      _statusBarAnimation = val;
    });
  }

  void statusBarStyleChanged(StatusBarStyle val) {
    this.setState(() {
      _statusBarStyle = val;
    });
    FlutterStatusbarManager.setStyle(val);
  }

  void colorNavBarChanged(Color val) {
    this.setState(() {
      _navBarColor = val;
    });
    updateNavBar();
  }

  void updateNavBar() {
    FlutterStatusbarManager.setNavigationBarColor(_navBarColor,
        animated: _navBarColorAnimated);
  }

  void navigationBarStyleChanged(NavigationBarStyle val) {
    this.setState(() {
      _navBarStyle = val;
    });
    FlutterStatusbarManager.setNavigationBarStyle(val);
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: const Text('Statusbar Manager example'),
        ),
        body: new Container(
          child: new Scrollbar(
            child: new ListView(
              padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0),
              children: <Widget>[
                renderTitle("Status Bar Height: $_statusBarHeight"),
                Divider(height: 25.0),
                renderTitle("Status Bar Color:"),
                SwitchListTile(
                  value: _statusBarColorAnimated,
                  title: new Text("Animated:"),
                  onChanged: (bool value) {
                    this.setState(() {
                      _statusBarColorAnimated = value;
                    });
                  },
                ),
                Text("Color:"),
                RadioListTile(
                    value: Colors.black,
                    title: Text("Black"),
                    onChanged: colorBarChanged,
                    dense: true,
                    groupValue: _statusBarColor),
                RadioListTile(
                    value: Colors.orange,
                    title: Text("Orange"),
                    onChanged: colorBarChanged,
                    dense: true,
                    groupValue: _statusBarColor),
                RadioListTile(
                    value: Colors.greenAccent,
                    title: Text("Green"),
                    onChanged: colorBarChanged,
                    dense: true,
                    groupValue: _statusBarColor),
                RadioListTile(
                    value: Colors.white30,
                    title: Text("White"),
                    onChanged: colorBarChanged,
                    dense: true,
                    groupValue: _statusBarColor),
                Text("Opacity:"),
                Slider(
                  value: _statusBarOpacity,
                  min: 0.0,
                  max: 1.0,
                  onChanged: (double val) {
                    this.setState(() {
                      _statusBarOpacity = val;
                    });
                    updateStatusBar();
                  },
                ),
                Divider(height: 25.0),
                renderTitle("Status Bar Hidden:"),
                SwitchListTile(
                  title: new Text("Hidden:"),
                  value: _statusBarHidden,
                  onChanged: (bool val) {
                    this.setState(() {
                      _statusBarHidden = val;
                    });
                    FlutterStatusbarManager.setHidden(_statusBarHidden,
                        animation: _statusBarAnimation);
                  },
                ),
                Text("Animation:"),
                RadioListTile(
                    value: StatusBarAnimation.NONE,
                    title: Text("NONE"),
                    onChanged: statusBarAnimationChanged,
                    dense: true,
                    groupValue: _statusBarAnimation),
                RadioListTile(
                    value: StatusBarAnimation.FADE,
                    title: Text("FADE"),
                    onChanged: statusBarAnimationChanged,
                    dense: true,
                    groupValue: _statusBarAnimation),
                RadioListTile(
                    value: StatusBarAnimation.SLIDE,
                    title: Text("SLIDE"),
                    onChanged: statusBarAnimationChanged,
                    dense: true,
                    groupValue: _statusBarAnimation),
                Divider(height: 25.0),
                renderTitle("Status Bar Style:"),
                RadioListTile(
                    value: StatusBarStyle.DEFAULT,
                    title: Text("DEFAULT"),
                    onChanged: statusBarStyleChanged,
                    dense: true,
                    groupValue: _statusBarStyle),
                RadioListTile(
                    value: StatusBarStyle.LIGHT_CONTENT,
                    title: Text("LIGHT_CONTENT"),
                    onChanged: statusBarStyleChanged,
                    dense: true,
                    groupValue: _statusBarStyle),
                RadioListTile(
                    value: StatusBarStyle.DARK_CONTENT,
                    title: Text("DARK_CONTENT"),
                    onChanged: statusBarStyleChanged,
                    dense: true,
                    groupValue: _statusBarStyle),
                Divider(height: 25.0),
                renderTitle("Status Bar Translucent:"),
                SwitchListTile(
                  title: new Text("Translucent:"),
                  value: _statusBarTranslucent,
                  onChanged: (bool val) {
                    this.setState(() {
                      _statusBarTranslucent = val;
                    });
                    FlutterStatusbarManager
                        .setTranslucent(_statusBarTranslucent);
                  },
                ),
                Divider(height: 25.0),
                renderTitle("Status Bar Activity Indicator:"),
                SwitchListTile(
                  title: new Text("Indicator:"),
                  value: _loadingIndicator,
                  onChanged: (bool val) {
                    this.setState(() {
                      _loadingIndicator = val;
                    });
                    FlutterStatusbarManager
                        .setNetworkActivityIndicatorVisible(_loadingIndicator);
                  },
                ),
                Divider(height: 25.0),
                renderTitle("Navigation Bar Color:"),
                SwitchListTile(
                  value: _navBarColorAnimated,
                  title: new Text("Animated:"),
                  onChanged: (bool value) {
                    this.setState(() {
                      _navBarColorAnimated = value;
                    });
                  },
                ),
                Text("Color:"),
                RadioListTile(
                    value: Colors.black,
                    title: Text("Black"),
                    onChanged: colorNavBarChanged,
                    dense: true,
                    groupValue: _navBarColor),
                RadioListTile(
                    value: Colors.orange,
                    title: Text("Orange"),
                    onChanged: colorNavBarChanged,
                    dense: true,
                    groupValue: _navBarColor),
                RadioListTile(
                    value: Colors.greenAccent,
                    title: Text("Green"),
                    onChanged: colorNavBarChanged,
                    dense: true,
                    groupValue: _navBarColor),
                RadioListTile(
                    value: Colors.white12,
                    title: Text("white"),
                    onChanged: colorNavBarChanged,
                    dense: true,
                    groupValue: _navBarColor),
                Divider(height: 25.0),
                renderTitle("Navigation Bar Style:"),
                RadioListTile(
                    value: NavigationBarStyle.DEFAULT,
                    title: Text("DEFAULT"),
                    onChanged: navigationBarStyleChanged,
                    dense: true,
                    groupValue: _navBarStyle),
                RadioListTile(
                    value: NavigationBarStyle.LIGHT,
                    title: Text("LIGHT"),
                    onChanged: navigationBarStyleChanged,
                    dense: true,
                    groupValue: _navBarStyle),
                RadioListTile(
                    value: NavigationBarStyle.DARK,
                    title: Text("DARK"),
                    onChanged: navigationBarStyleChanged,
                    dense: true,
                    groupValue: _navBarStyle),
                Divider(height: 25.0),
                renderTitle("Fullscreen mode:"),
                SwitchListTile(
                  title: new Text("Fullscreen:"),
                  value: _fullscreenMode,
                  onChanged: (bool val) {
                    this.setState(() {
                      _fullscreenMode = val;
                    });
                    FlutterStatusbarManager.setFullscreen(_fullscreenMode);
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
41
votes
answers
27 views
+10

How can I layout widgets based on the size of the parent?

Lets say you have a parent widget that might have variable size.

For example:

var container = new Container(
   height: 200.0, // Imagine this might change
   width: 200.0, // Imagine this might change
   // Imagine content in this container will 
   // depend on the parent container
   child: new Container(), 
);

And maybe you want to have the child of the parent container to render something different based on what the size that it's given.

Think of responsive design breakpoints, if the width is over X use this layout, if the width is under X use that layout.

What's the best way to do this in Flutter?

up vote 41 down vote accepted favorite
沙发
+410
+50

You will want to use the LayoutBuilder widget which will build at layout time and provides the parent widget's constraints.

The LayoutBuilder takes in a build() function which has the the standard BuildContext along with the BoxConstraints as parameters that can be used to help dynamically render widgets based on size.

Let's build a simple example of widget that renders "LARGE" if the parent width is greater than 200px and "SMALL" if the parent width is less or equal to that.

var container = new Container(
  // Toggling width from 100 to 300 will change what is rendered
  // in the child container
  width: 100.0,
  // width: 300.0
  child: new LayoutBuilder(
    builder: (BuildContext context, BoxConstraints constraints) {
      if(constraints.maxWidth > 200.0) {
        return new Text('BIG');
      } else {
        return new Text('SMALL');
      }
    }
  ),
);

謝謝,我一直在努力根據父窗口小部件的大小來佈局窗口小部件,首先我嘗試使用屏幕高度減去appbar高度和底部欄高度,這是愚蠢的,不適用於所有物理手機。 - Daniel Dai 3月28日1:59

66
votes
answers
26 views
+10

How to change the appBar back button color

I cannot figure out how to change the appBar's automatic back button to a different color. its under a scaffold and i've tried to research it but i cant wrap my head around it.

return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        title: Image.asset(
          'images/.jpg',
          fit: BoxFit.fill,
        ),
        centerTitle: true,
      ),
up vote 47 down vote accepted favorite
沙发
+470
+50

You have to use the iconTheme property from the AppBar , like this:

  appBar: AppBar(
          iconTheme: IconThemeData(
            color: Colors.black, //change your color here
          ),
          title: Text("Sample"),
          centerTitle: true,
        ),
        body: Text("Sample body"),
      );

這似乎是正確的方法。應該被接受。 - pblead26 18年8月16日19:47

+140

you can also override the default back arrow with a widget of your choice, via 'leading':

leading: new IconButton(
  icon: new Icon(Icons.arrow_back, color: Colors.orange),
  onPressed: () => Navigator.of(context).pop(),
), 

all the AppBar widget does is provide a default 'leading' widget if it's not set.

不完全正確,因為當按下ModalRoute時,AppBar也會顯示後退按鈕。 - creativecreatorormaybenot 18年8月15日19:23

並在AppBar中設置automaticImplyLeading:false。 - Loolooii 18年12月26日13:50

Upvoted for Navigator.of(context).pop(); 謝謝傢伙 - Fadhly Permata 1月23日8:44

+50

It seemed to be easier to just create a new button and add color to it, heres how i did it for anyone wondering

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: BackButton(
            color: Colors.black
        ),
0
votes
answers
28 views
+10

Dart Multiple Constructors

Is it really not possible to create multiple constructors for a class in dart?

in my Player Class, If I have this constructor

Player(String name, int color) {
    this._color = color;
    this._name = name;
}

Then I try to add this constructor:

Player(Player another) {
    this._color = another.getColor();
    this._name = another.getName();
}

I get the following error:

The default constructor is already defined.

I'm not looking for a workaround by creating one Constructor with a bunch of non required arguments.

Is there a nice way to solve this?

68
votes
answers
24 views
+10

How to change the app display name build with flutter?

I have created the app using flutter create testapp. Now, I want to change the App name from "testapp" to "My Trips Tracker". How can I do that ?

I have tried changing from the AndroidManifest.xml and It got changed but is there a way that flutter provides to do that ?

沙发
+360

The Flutter documentation points out where you can change the display name of your application for both Android and iOS. This may be what you are looking for:

For Android

It seems you have already found this in the AndroidManifest.xml as the application entry.

Review the default App Manifest file AndroidManifest.xml located in /android/app/src/main/ and verify the values are correct, especially:

application: Edit the application tag to reflect the final name of the app.

For iOS

See the Review Xcode project settings section:

Navigate to your target’s settings in Xcode:

In Xcode, open Runner.xcworkspace in your app’s ios folder.

To view your app’s settings, select the Runner project in the Xcode project navigator. Then, in the main view sidebar, select the Runner target.

Select the General tab. Next, you’ll verify the most important settings:

Display Name: the name of the app to be displayed on the home screen and elsewhere.

這些是本機方法。我想知道顫動是否從一個點處理它? - Pankaj Bansal 4月29日6:45

@AshtonThomas我可以改變這個但是在清理重建等之後,轉輪應用程序重置它的值,我怎麼能保持這個?(據我所知,Runner項目是在乾淨的構建之後重新創建的) - Johnykutty 5月19日23:28

板凳
+140

You can change it in iOS without opening Xcode by editing the project/ios/Runner/info.plist <key>CFBundleDisplayName</key> to the String that you want as your name.

FWIW - I was getting frustrated with making changes in Xcode and Flutter, so I started committing all changes before opening Xcode, so I could see where the changes show up in the Flutter project.

地板
+90

Android

Open AndroidManifest.xml (located at android/app/src/main)

<application
    android:label="App Name" ...> // Your app name here

iOS

Open info.plist file

<key>CFBundleName</key>
<string>App Name</string> // Your app name here

對於Android,這不起作用。試圖改變 至 ,錯誤信息sais:清單合併失敗:來自AndroidManifest.xml的屬性應用程序@ label value =(OldName):AndroidManifest.xml中也存在21:9-32:21:9-32 value =(NewName)..什麼你有沒有建議? - iKK 5月28日17:46

您的項目中可能有多個AndroidManifest.xml文件,您必須打開位於android / app / src / main的文件。 - CopsOnRoad 5月29日6:10

謝謝,這就是它!我在Debug下有另一個Manifest.xml。現在它有效! - iKK 5月29日9:33

我沒有投票!但是,我贊成你的“多個AndroidManifest”答案,現在也是你原來的答案。再次,謝謝。 - iKK 5月29日9:38

4楼
+60
  • Review the default App Manifest file AndroidManifest.xml located in <app dir>/android/app/src/main

  • Edit the android:label to your desire display name

5楼
+30

One problem is that in iOS Settings (iOS 12.x) if you change the Display Name, it leaves the app name and icon in iOS Settings as the old version.

47
votes
answers
41 views
+10

How to implement drop down list in flutter?

I have a list of locations that i want to implement as a dropdown list in Flutter. Im pretty new to the language. Here's what i have done.

new DropdownButton(
  value: _selectedLocation,
  onChanged: (String newValue) {
    setState(() {
      _selectedLocation = newValue;
     });
},
items: _locations.map((String location) {
  return new DropdownMenuItem<String>(
     child: new Text(location),
  );
}).toList(),

This is my list of items:

List<String> _locations = ['A', 'B', 'C', 'D'];

And I am getting the following error.

Another exception was thrown: 'package:flutter/src/material/dropdown.dart': Failed assertion: line 468 pos 15: 'value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not true.

I assume the value of _selectedLocation is getting null. But i am initialising it like so.

String _selectedLocation = 'Please choose a location';

沙发
+250

Try this

new DropdownButton<String>(
  items: <String>['A', 'B', 'C', 'D'].map((String value) {
    return new DropdownMenuItem<String>(
      value: value,
      child: new Text(value),
    );
  }).toList(),
  onChanged: (_) {},
)
板凳
+60

First of all, let's investigate what the error says (I have cited the error that's thrown with Flutter 1.2, but the idea is the same):

Failed assertion: line 560 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.

There are four or conditions. At least one of them must be fulfilled:

  • Items (a list of DropdownMenuItem widgets) were provided. This eliminates items == null.
  • Non-empty list was provided. This eliminates items.isEmpty.
  • A value (_selectedLocation) was also given. This eliminates value == null. Note that this is DropdownButton's value, not DropdownMenuItem's value.

Hence only the last check is left. It boils down to something like:

Iterate through DropdownMenuItem's. Find all that have a value that's equal to _selectedLocation. Then, check how many items matching it were found. There must be exactly one widget that has this value. Otherwise, throw an error.

The way code is presented, there is not a DropdownMenuItem widget that has a value of _selectedLocation. Instead, all the widgets have their value set to null. Since null != _selectedLocation, last condition fails. Verify this by setting _selectedLocation to null - the app should run.

To fix the issue, we first need to set a value on each DropdownMenuItem (so that something could be passed to onChanged callback):

return DropdownMenuItem(
    child: new Text(location),
    value: location,
);

The app will still fail. This is because your list still does not contain _selectedLocation's value. You can make the app work in two ways:

  • Option 1. Add another widget that has the value (to satisfy items.where((DropdownMenuItem<T> item) => item.value == value).length == 1). Might be useful if you want to let the user re-select Please choose a location option.
  • Option 2. Pass something to hint: paremter and set selectedLocation to null (to satisfy value == null condition). Useful if you don't want Please choose a location to remain an option.

See the code below that shows how to do it:

import 'package:flutter/material.dart';

void main() {
  runApp(Example());
}

class Example extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
//  List<String> _locations = ['Please choose a location', 'A', 'B', 'C', 'D']; // Option 1
//  String _selectedLocation = 'Please choose a location'; // Option 1
  List<String> _locations = ['A', 'B', 'C', 'D']; // Option 2
  String _selectedLocation; // Option 2

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: DropdownButton(
            hint: Text('Please choose a location'), // Not necessary for Option 1
            value: _selectedLocation,
            onChanged: (newValue) {
              setState(() {
                _selectedLocation = newValue;
              });
            },
            items: _locations.map((location) {
              return DropdownMenuItem(
                child: new Text(location),
                value: location,
              );
            }).toList(),
          ),
        ),
      ),
    );
  }
}
地板
+50

You need to add value: location in your code to work it. Check this out.

items: _locations.map((String location) {
  return new DropdownMenuItem<String>(
     child: new Text(location),
     value: location,
  );
}).toList(),
4楼
+50

you have to take this into account (from DropdownButton docs):

"The items must have distinct values and if value isn't null it must be among them."

So basically you have this list of strings

List<String> _locations = ['A', 'B', 'C', 'D'];

And your value in Dropdown value property is initialised like this:

String _selectedLocation = 'Please choose a location';

Just try with this list:

List<String> _locations = ['Please choose a location', 'A', 'B', 'C', 'D'];

That should work :)

Also check out the "hint" property if you don't want to add a String like that (out of the list context), you could go with something like this:

DropdownButton<int>(
          items: locations.map((String val) {
                   return new DropdownMenuItem<String>(
                        value: val,
                        child: new Text(val),
                         );
                    }).toList(),
          hint: Text("Please choose a location"),
          onChanged: (newVal) {
                  _selectedLocation = newVal;
                  this.setState(() {});
                  });
5楼
+40

place the value inside the items.then it will work,

new DropdownButton<String>(
              items:_dropitems.map((String val){
                return DropdownMenuItem<String>(
                  value: val,
                  child: new Text(val),
                );
              }).toList(),
              hint:Text(_SelectdType),
              onChanged:(String val){
                _SelectdType= val;
                setState(() {});
                })
6楼
+20

You can use DropDownButton class in order to create drop down list :

...
...
String dropdownValue = 'One';
...
...
Widget build(BuildContext context) {
return Scaffold(
  body: Center(
    child: DropdownButton<String>(
      value: dropdownValue,
      onChanged: (String newValue) {
        setState(() {
          dropdownValue = newValue;
        });
      },
      items: <String>['One', 'Two', 'Free', 'Four']
          .map<DropdownMenuItem<String>>((String value) {
        return DropdownMenuItem<String>(
          value: value,
          child: Text(value),
        );
      }).toList(),
    ),
  ),
);
...
...

please refer to this flutter web page

7楼
0

I was facing a similar issue with the DropDownButton when i was trying to display a dynamic list of strings in the dropdown. I ended up creating a plugin : flutter_search_panel. Not a dropdown plugin, but you can display the items with the search functionality.

Use the following code for using the widget :

    FlutterSearchPanel(
        padding: EdgeInsets.all(10.0),
        selected: 'a',
        title: 'Demo Search Page',
        data: ['This', 'is', 'a', 'test', 'array'],
        icon: new Icon(Icons.label, color: Colors.black),
        color: Colors.white,
        textStyle: new TextStyle(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 20.0, decorationStyle: TextDecorationStyle.dotted),
        onChanged: (value) {
          print(value);
        },
   ),

如果這是一個合理的解決方案,讓StatefulWidget將它的狀態存儲在屬性中也是合理的嗎? - 馬修史密斯於2017年9月5日18:17

問題在於createState()是由flutter調用的生命週期方法,所以你不應該自己調用它並存儲返回的狀態。 - xqwzts 17年5月5日19:24

我用這種方法看到的問題是,它需要我們的通用控制器成為Widget樹的一部分,這違背了我們的設計目標,即可以在Web上重用它們。似乎Flutter正在強制任何需要將窗口小部件的狀態更改為窗口小部件的東西。 - 馬修史密斯於2017年9月5日21:27

@MatthewSmith你找到了代碼共享的解決方案,還是需要一些指針?:) - RémiRousselet18年10月3日1:26

這種策略不起作用。您無法看到公開的狀態方法:[dart]無法通過實例訪問靜態方法“myStaticMethod”。[instance_access_to_static_member] - Alessandro Ornano 1月19日13:14

8楼
0

Let say we are creating a drop down list of currency:

List _currency = ["INR", "USD", "SGD", "EUR", "PND"];
List<DropdownMenuItem<String>> _dropDownMenuCurrencyItems;
String _currentCurrency;

List<DropdownMenuItem<String>> getDropDownMenuCurrencyItems() {
  List<DropdownMenuItem<String>> items = new List();
  for (String currency in _currency) {
    items.add(
      new DropdownMenuItem(value: currency, child: new Text(currency)));
  }
  return items;
}

void changedDropDownItem(String selectedCurrency) {
  setState(() {
    _currentCurrency = selectedCurrency;
  });
}

Add below code in body part:

new Row(children: <Widget>[
  new Text("Currency: "),
  new Container(
    padding: new EdgeInsets.all(16.0),
  ),
  new DropdownButton(
    value: _currentCurrency,
    items: _dropDownMenuCurrencyItems,
    onChanged: changedDropDownItem,
  )
])

謝謝,第一種方法非常有效。我使用它來從子窗口小部件導航到不同的屏幕,而不使用傳統的材料導航堆棧... - Janpan 18年12月17日在9:51

9楼
0

The error you are getting is due to ask for a property of a null object. Your item must be null so when asking for its value to be compared you are getting that error. Check that you are getting data or your list is a list of objects and not simple strings.

10楼
0

When I ran into this issue of wanting a less generic DropdownStringButton, I just created it:

dropdown_string_button.dart

import 'package:flutter/material.dart';
// Subclass of DropdownButton based on String only values.
// Yes, I know Flutter discourages subclassing, but this seems to be
// a reasonable exception where a commonly used specialization can be
// made more easily usable.
//
// Usage: 
// DropdownStringButton(items: ['A', 'B', 'C'], value: 'A', onChanged: (string) {})
//
class DropdownStringButton extends DropdownButton<String> {
  DropdownStringButton({
    Key key, @required List<String> items, value, hint, disabledHint,
    @required onChanged, elevation = 8, style, iconSize = 24.0, isDense = false,
    isExpanded = false, }) : 
    assert(items == null || value == null || items.where((String item) => item == value).length == 1),
        super(
          key: key,
          items: items.map((String item) {
            return DropdownMenuItem<String>(child: Text(item), value: item);
          }).toList(),
        value: value, hint: hint, disabledHint: disabledHint, onChanged: onChanged,
        elevation: elevation, style: style, iconSize: iconSize, isDense: isDense,
        isExpanded: isExpanded,
        );
    }
11楼
0

It had happened to me when I replace the default value with a new dynamic value. But, somehow your code may be dependent on that default value. So try keeping a constant with default value stored somewhere to fallback.

const defVal = 'abcd';
String dynVal = defVal;

// dropdown list whose value is dynVal that keeps changing with onchanged
// when rebuilding or setState((){})

dynVal = defVal;
// rebuilding here...
12楼
0

Change

List<String> _locations = ['A', 'B', 'C', 'D'];

To

List<String> _locations = [_selectedLocation, 'A', 'B', 'C', 'D'];

_selectedLocation needs to be part of your item List;

39
votes
answers
32 views
+10

Dart: mapping a list (list.map)

I have a list of Strings, e.g.,

var moviesTitles = ['Inception', 'Heat', 'Spider Man'];

and wanted to use moviesTitles.map to convert them to a list of Tab Widgets in Flutter.

up vote 39 down vote accepted favorite
沙发
+390
+50

you can use

moviesTitles.map((title) => Tab(text: title)).toList()

example:

    bottom: new TabBar(
      controller: _controller,
      isScrollable: true,
      tabs:
        moviesTitles.map((title) => Tab(text: title)).toList()
      ,
    ),

為什麼我們需要toList? - onmyway133 3月12日8:25

@ onmyway133因為問題是關於映射列表 - AbdulMomenعبدالمؤمن3月12日11:07

@AbdulMomenعبدالمؤمن我相信問題是為什麼首先需要它。在Swift中,映射一個數組 返回一個數組新結果類型。為什麼映射Dart列表的結果還沒有列表? - 邁克爾朗4月18日15:42

@MichaelLong因為在這裡,在Dart,地圖 返回一個Iterable 而不是列表 - AbdulMomenعبدالمؤمن4月20日5:40

46
votes
answers
27 views
+10

Flutter ListView lazy loading

How can I realize items lazy loading for endless listview? I want to load more items by network when user scroll to the end of listview.

up vote 32 down vote accepted favorite
沙发
+320
+50

You can listen to a ScrollController.

ScrollController has some useful information, such as the scrolloffset and a list of ScrollPosition.

In your case the interesting part is in controller.position which is the currently visible ScrollPosition. Which represents a segment of the scrollable.

ScrollPosition contains informations about it's position inside the scrollable. Such as extentBefore and extentAfter. Or it's size, with extentInside.

Considering this, you could trigger a server call based on extentAfter which represents the remaining scroll space available.

Here's an basic example using what I said.

class MyHome extends StatefulWidget {
  @override
  _MyHomeState createState() => new _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  ScrollController controller;
  List<String> items = new List.generate(100, (index) => 'Hello $index');

  @override
  void initState() {
    super.initState();
    controller = new ScrollController()..addListener(_scrollListener);
  }

  @override
  void dispose() {
    controller.removeListener(_scrollListener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Scrollbar(
        child: new ListView.builder(
          controller: controller,
          itemBuilder: (context, index) {
            return new Text(items[index]);
          },
          itemCount: items.length,
        ),
      ),
    );
  }

  void _scrollListener() {
    print(controller.position.extentAfter);
    if (controller.position.extentAfter < 500) {
      setState(() {
        items.addAll(new List.generate(42, (index) => 'Inserted $index'));
      });
    }
  }
}

You can clearly see that when reaching the end of the scroll, it scrollbar expends due to having loaded more items.

是的,它看起來很好,我會嘗試一下。但我有疑問。當items.length被更改時,itemCount(itemBuilder參數)會動態更改嗎? - Valentin Schukin 18年3月28日10:27

順便說一下,我應該總是在功能體的末尾調用超級函數的“超級”調用嗎?這是一些非常有趣但我非常感興趣) - Valentin Schukin於18年3月28日10:33

ItemCount會隨著時間的推移而更新,是的。不總是需要調用超級。但有些功能要求它。 - RémiRousselet18年3月28日10:40

所以我嘗試過並且有效。謝謝你) - Valentin Schukin 18年3月28日10:43

@RémiRousselet一個小提示,super.initState()應該始終是initState()方法的第一行。讓我更新你的答案。 - CopsOnRoad 1月15日7:29

+120

Thanks for Rémi Rousselet's approach, but it does not solve all the problem. Especially when the ListView has scrolled to the bottom, it still calls the scrollListener a couple of times. The better approach is to combine Notification Listener with Remi's approach. Here is my solution:

bool _handleScrollNotification(ScrollNotification notification) {
  if (notification is ScrollEndNotification) {
    if (_controller.position.extentAfter == 0) {
      loadMore();
    }
  }
  return false;
}

@override
Widget build(BuildContext context) {
    final Widget gridWithScrollNotification = NotificationListener<
            ScrollNotification>(
        onNotification: _handleScrollNotification,
        child: GridView.count(
            controller: _controller,
            padding: EdgeInsets.all(4.0),
          // Create a grid with 2 columns. If you change the scrollDirection to
          // horizontal, this would produce 2 rows.
          crossAxisCount: 2,
          crossAxisSpacing: 2.0,
          mainAxisSpacing: 2.0,
          // Generate 100 Widgets that display their index in the List
          children: _documents.map((doc) {
            return GridPhotoItem(
              doc: doc,
            );
          }).toList()));
    return new Scaffold(
      key: _scaffoldKey,
      body: RefreshIndicator(
       onRefresh: _handleRefresh, child: gridWithScrollNotification));
}

是的,這是我正在尋找的那個。謝謝!! - surenyonjan 3月24日3:22

天才謝謝! - 在4月5日7點15分,卡住了

+20

The solution use ScrollController and I saw comments mentioned about page.
I would like to share my finding about package incrementally_loading_listview https://github.com/MaikuB/incrementally_loading_listview.
As packaged said : This could be used to load paginated data received from API requests.

Basically, when ListView build last item and that means user has scrolled down to the bottom.
Hope it can help someone who have similar questions.

For purpose of demo, I have changed example to let a page only include one item and add an CircularProgressIndicator.

enter image description here

...
bool _loadingMore;
bool _hasMoreItems;
int  _maxItems = 30;
int  _numItemsPage = 1;
...
_hasMoreItems = items.length < _maxItems;    
...
return IncrementallyLoadingListView(
              hasMore: () => _hasMoreItems,
              itemCount: () => items.length,
              loadMore: () async {
                // can shorten to "loadMore: _loadMoreItems" but this syntax is used to demonstrate that
                // functions with parameters can also be invoked if needed
                await _loadMoreItems();
              },
              onLoadMore: () {
                setState(() {
                  _loadingMore = true;
                });
              },
              onLoadMoreFinished: () {
                setState(() {
                  _loadingMore = false;
                });
              },
              loadMoreOffsetFromBottom: 0,
              itemBuilder: (context, index) {
                final item = items[index];
                if ((_loadingMore ?? false) && index == items.length - 1) {
                  return Column(
                    children: <Widget>[
                      ItemCard(item: item),
                      Card(
                        child: Padding(
                          padding: const EdgeInsets.all(16.0),
                          child: Column(
                            children: <Widget>[
                              Row(
                                crossAxisAlignment:
                                    CrossAxisAlignment.start,
                                children: <Widget>[
                                  Container(
                                    width: 60.0,
                                    height: 60.0,
                                    color: Colors.grey,
                                  ),
                                  Padding(
                                    padding: const EdgeInsets.fromLTRB(
                                        8.0, 0.0, 0.0, 0.0),
                                    child: Container(
                                      color: Colors.grey,
                                      child: Text(
                                        item.name,
                                        style: TextStyle(
                                            color: Colors.transparent),
                                      ),
                                    ),
                                  )
                                ],
                              ),
                              Padding(
                                padding: const EdgeInsets.fromLTRB(
                                    0.0, 8.0, 0.0, 0.0),
                                child: Container(
                                  color: Colors.grey,
                                  child: Text(
                                    item.message,
                                    style: TextStyle(
                                        color: Colors.transparent),
                                  ),
                                ),
                              )
                            ],
                          ),
                        ),
                      ),
                      Center(child: CircularProgressIndicator())
                    ],
                  );
                }
                return ItemCard(item: item);
              },
            );

full example https://github.com/MaikuB/incrementally_loading_listview/blob/master/example/lib/main.dart

Package use ListView index = last item and loadMoreOffsetFromBottom to detect when to load more.

    itemBuilder: (itemBuilderContext, index) {    
              if (!_loadingMore &&
              index ==
                  widget.itemCount() -
                      widget.loadMoreOffsetFromBottom -
                      1 &&
              widget.hasMore()) {
            _loadingMore = true;
            _loadingMoreSubject.add(true);
          }
65
votes
answers
35 views
+10

How can I dismiss an AlertDialog on a FlatButton click?

I have the following AlertDialog.

showDialog(
            context: context,
            child: new AlertDialog(
              title: const Text("Location disabled"),
              content: const Text(
                  """
Location is disabled on this device. Please enable it and try again.
                  """),
              actions: [
                new FlatButton(
                  child: const Text("Ok"),
                  onPressed: _dismissDialog,
                ),
              ],
            ),
        );

How can I make _dismissDialog() dismiss said AlertDialog?

up vote 46 down vote accepted favorite
沙发
+460
+50

Navigator.pop() should do the trick. You can also use that to return the result of the dialog (if it presented the user with choices)

謝謝,這很有效。調用Navigator.pop()會按預期關閉對話框。我當前的onPressed如下:onPressed :()=> Navigator.pop(context), - Gustash 2017年5月24日13:52

完美地工作 - Muhammad Faizan 18年12月27日5:02

+130
Navigator.of(context, rootNavigator: true).pop('dialog')

worked with me.

接受的答案導致我的整個頁面消失,這是隱藏對話的正確答案 - user969068 Feb 17 at 11:12

這是一個更好的方法來關閉對話框,我嘗試上面的解決方案,但它正在彈出我的其他視圖。 - Farhana 3月1日6:29

+40

You can use any of the following:

Navigator.of(context).pop();
Navigator.pop(context);
+20
Navigator.pop(_)

worked for me, but the Flutter Team's gallery contains an example using:

Navigator.of(context, rootNavigator: true).pop()

which also works, and I am tempted to follow their lead.

0

The accepted answer states how to dismiss a dialog using the Navigator Class. To dismiss a dialog without using Navigator you can set the onPressed event of the button to the following:

setState((){
  thisAlertDialog = null; 
});

In case the code above is not self-explanatory it is basically setting the Parent AlertDialog of the FlatButton to null, thus dismissing it.

87
votes
answers
29 views
+10

InkWell not showing ripple effect

Tapping the container triggers the onTap() handler but does not show any ink splash effect.

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new InkWell(
          onTap: (){print("tapped");},
          child: new Container(
            width: 100.0,
            height: 100.0,
            color: Colors.orange,
          ),
        ),
      ),
    );
  }
}

I tried putting the InkWell inside the Container as well but in vain.

up vote 53 down vote accepted favorite
沙发
+530
+50

I think adding a color to the container is covering over the ink effect

https://docs.flutter.io/flutter/material/InkWell/InkWell.html

This code seems to work

  body: new Center(
    child: new Container(
      child: new Material(
        child: new InkWell(
          onTap: (){print("tapped");},
          child: new Container(
            width: 100.0,
            height: 100.0,
          ),
        ),
        color: Colors.transparent,
      ),
      color: Colors.orange,
    ),
  ),

just click the middle square.

Edit: I found the bugreport. https://github.com/flutter/flutter/issues/3782

This is actually as expected, though we should update the docs to make it clearer.

What's going on is that the Material spec says that the splashes are actually ink on the Material. So when we splash, what we do is we literally have the Material widget do the splash. If you have something on top of the Material, we splash under it, and you can't see it.

I have wanted to add a "MaterialImage" widget which conceptually prints its image into the Material as well, so that then the splashes would be over the image. We could have a MaterialDecoration which does something similar for a Decoration. Or we could have Material itself take a decoration. Right now it takes a color, but we could extend that to taking a whole decoration. It's not clear whether it's really material-spec-compatible to have a material with a gradient, though, so I'm not sure whether we should do that.

On the short run, if you just need a workaround, you can put a Material on top of the container, with the material set to use the "transparency" type, and then put the ink well inside that.

--hixie

您可以將顏色放在材料本身上並省略容器。 - Herohtar 2月5日4:13

當CopsOnRoad發布他的答案時,我有點想通了。 - user1462442 2月5日12:19

你救了我一個輕微的頭痛 - Koen Van Looveren 4月21日9:04

@KoenVanLooveren嘗試CopsOnRoad的答案。我沒有編輯我的解決方案,因為CopsOnRoad的解決方案非常優雅 - user1462442 4月21日14:31

這就是我在一些重構之後使用的:D謝謝 - Koen Van Looveren 4月22日7:18

+300

Step 1. Use Material as parent for your InkWell, otherwise it won't show ripple effects.

Step 2. Give color to the Material widget, this is going to be the color you were using in your Container.

Step 3. Don't forget to use onTap() even if you don't have anything to handle.

Simple example:

Widget myWidget() {
  return Material( // needed
    color: Colors.orange,
    child: InkWell(
      onTap: () {}, // needed
      child: Container(width: 100.0, height: 100.0),
    ),
  );
}

源確認步驟3:github.com/flutter/flutter/blob / ...在每個處理程序中,他們檢查回調是否可用,否則沒有任何反應。 - 1819年11月19日晚上14:24

+20

InkWell() will never show the ripple effect until you add the

onTap : () {} 

parameter inside the InkWell as it starts listening to your taps only when you specify this parameter.

你自己嘗試過嗎? - CopsOnRoad 18年10月2日10:39

是的,我做了,它是真的。檢查源:github.com/flutter/flutter/blob / ...在每個處理程序中,他們檢查回調是否可用,否則沒有任何反應。 - 1819年11月19日晚上14:23

@Nightking,當你把你的代碼放在OP代碼中時,它真的顯示出漣漪效應嗎? - CopsOnRoad 18年12月13日18:49

+20

I ran into this same problem trying to create an alternating color of InkWell's in a ListView. In that particular case, there's a simple solution: wrap the alternates in a Container that uses a mostly transparent tint/brightness change -- the InkWell touch animation will still be visible beneath it. No Material needed. Note there are other issues when trying to work around this with a Materal -- e.g., it will override a DefaultTextStyle you're using with the default (it installs an AnimatedDefaultTextStyle) which is a huge pain.

0

A better way is to use the Ink widget instead of any other widget.

Instead of defining color inside container you can define it in Ink widget itself.

Below code will work.

Ink(
  color: Colors.orange,
  child: InkWell(
    child: Container(
      width: 100,
      height: 100,
    ),
    onTap: () {},
  ),
)