Flutter chat app with django channels backend keeps reloading - django

I am using flutter chat app and using Django Rest Framework + channels as the backend. In the chat page, I am using the following code to display messages -
Scaffold(
backgroundColor: backColor,
appBar: AppBar(),
body:
GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).primaryColor,
),
),
);
}else{
var mymessages = jsonDecode(snapshot.data);
if(mymessages['command']=='messages'){
// setState(() {
messages = ChatMessagesList.fromJson(mymessages["messages"]).messages;
// });
}else{
ChatMessage msg = ChatMessage.fromJson(mymessages["message"]);
if(!messages.contains(msg))
// setState(() {
messages.add(msg);
// });
}
return Column(
children: <Widget>[
Expanded(
child: Container(
decoration: BoxDecoration(
color: backColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
child:
ListView.builder(
reverse: true,
padding: EdgeInsets.only(top: 15.0),
itemCount: messages.length,
itemBuilder: ( context, index) {
final ChatMessage message = messages[index];
final bool isMe = message.sender.username == linUser.username;
return _buildMessage(message, isMe);
},
),
),
),
),
_buildMessageComposer(),
],
);
}
}
)
,
),
)
Here channel is IOWebSocketChannel that connect to django websocket url, and it is closed in dispose(). messages is a List<ChatMessage> declared outside build. Depending on what is sent via channel.sink either list of ChatMessage is received or just a single, latest ChatMessage sent by users.
Problem is, as long as the list of messages has a message, the chat page as well as all the previous pages in the stack start reloading/refreshing continuously, the displayed page itself stays at the chat page, but in the terminal, the print statements from earlier pages like Home Page and ChatRooms page keep showing up repeatedly, non stop, without any error. I have tried a few methods like using rest api to load the past messages, but the moment there is a new message on channel.stream, the reload/refresh starts. It only stops when I navigate back to previous page i.e. the ChatRooms page. I tried using stream controller but that went nowhere.
I also checked some examples that use firebase, and realized that their stream receives the entire list of messages everytime. What I am trying to do is fetch the list of past messages once at the beginning, save it inside messages, and then keep adding to the list as and when messages are sent/received. The same will be reflected in the page.

Related

Load and cache images from AWS S3 into flutter

I want to fetch and cache user profile pictures from S3 into my flutter app.
First, when a user uploads a picture, my flask backend generates a random file name, stores the file in an S3 bucket (using boto3) and the name in the database.
To retrieve the picture I use presigned_urls:
s3client = boto3.client('s3', config=Config(signature_version='s3v4', region_name='eu-west-2'))
s3client.generate_presigned_url('get_object',Params={'Bucket': BUCKET,'Key': file_name_retrieved_from_db_for_user},ExpiresIn=120)
In Flutter I have a Future which calls the API and gets the image's generated presigned url (i.e. https://xx.s3.amazonaws.com/FILENAME.jpg?signature).
And then using a FutureBuilder I do the following:
FutureBuilder(
future: get_picture_url(user_id),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data==0) {
return Icon(Icons.account_circle, size: 110.0);
}
print(user_id);
print('this is the data fetched');
print(snapshot.data);
return CachedNetworkImage(
imageUrl: snapshot.data,
imageBuilder: (context, imageProvider) => Container(
width: 180.0,
height: 180.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: imageProvider, fit: BoxFit.cover),
),
),
placeholder: (context, url) => ProfPicPlaceHolder(),
errorWidget: (context, url, error) => Icon(Icons.error),
);
} else {
return ProfPicPlaceHolder();
}
}
),
The problem is that each time the FutureBuilder calls the API to get the image's url, the URL is different due to different signature following the filename in the url, so the same image is loaded and cached again and again.
How can I access an image that is stored in S3 in flask using boto3 and then pass that url to cached network image in flutter?
Is there any other way to cache an image in flutter from aws S3?
I implemented a solution.
For those dealing with the same thing, use the property cacheKey of the CachedNetworkImage().
I used the filename (stored in user's table) as cacheKey. Also, I cache filename and file url in a hivebox which is updated regularly.
My view reads data from the hivebox.

How do I display name (without extension) of files from assets folders in flutter

I have some music files in my assets folder and I want to automatically display them in a list, without having to write a line for every single music in the folder.
I think something like ListView.builder might work, but I'm pretty new in all this and not quite sure how to execute this properly.
Since it's impossible to get the names of your music files from the /assets folder within a Flutter project, I think you can try these ways:
Have a json file listing all the metadata of the files (name, artist, path in /assets, etc) and work with that file (get the name, get the path of the file, etc).
Store your music online and get them through APIs
Copy all the music from your /assets into a phone's directory, then handle the files from that directory from now on (For example: Call this method to get the names of the files). Check out this answer.
If what you want to display is like the image you describe, and if the name is not important (like ringtones), then you can simply do this (assuming you name the files by numbers from 1-10):
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: SampleScreen(),
));
}
class SampleScreen extends StatefulWidget {
#override
_SampleScreenState createState() => _SampleScreenState();
}
class _SampleScreenState extends State<SampleScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => _buildMusicItem(index)),
);
}
Widget _buildMusicItem(int index) {
return Container(
height: 40,
margin: EdgeInsets.all(5),
padding: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.red, width: 2)),
alignment: Alignment.centerLeft,
child: Text('Music ${index + 1}'),
);
}
}
Result:

Setting a background in flutter from a list

I would like to have a page called Theme with grid images, and when I click on an image, to select that image as my background to home page.
This is a list I have:
List<Theme2> themeitems = [
Theme2(
background: 'assets/images/theme/deskWhite.png',
selected: false,
),
Theme2(
background: 'assets/images/theme/greenDrop.png',
selected: false,
),
Theme2(
background: 'assets/images/theme/leaf.png',
selected: false,
),
],
This is a home page where I would like to set a background image:
child: Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/theme/sand.png'),
////// image: AssetImage(themeitems[idx].background.selected), // I know this is not correct
fit: BoxFit.cover,
),
),
child: Column(.........
So, how could I set background image from one of these list items?
I tried using themeitems[index].selected = !themeitems[index].selected; but I am not sure how to use it correctly. If there is a problem like this on the net, please send me a link.
Thank you for your time.
Based on your code, I think it's easier to rename your images into something more sequentially, for example: image1.png, image2.png, image3.png,...
You can store it in a list, but I think you don't need to do that, as long as you've added your images in your assets folder.
And then, to select the image, inside your code:
int index = 0; //this variable is to store your selected index
child: Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/theme/image$index.png'), //So if the index = 0, so image).png will appear. And, so on.
fit: BoxFit.cover,
),
),
child: Column( //YOUR CODE

Why is window.open() returning null in unit test?

I'm writing a unit test for a method that launches a browser window when a user clicks a button.
The test fails because null is returned.
When running the test no browser window is opened.
When using the app it works as expected.
I've seen a lot of the other SO questions regarding window.open() returning null or otherwise not doing what the OP wanted, but these all have to do with getting the browser to do specific things. The code I'm testing does what I want it to do when used in the web app, but I'm not able to write a passing unit test for it. This unit test is a step along the way to fixing a problem that occurs in the step after this method runs, which is why I want to understand why this test isn't passing.
The top answer here lead me to think that the browser might be blocking a new window because the request is coming from outside the browser, which would result in a null return. I disabled popup blocking in Chrome just in case.
This is main.dart:
import 'package:flutter_web/material.dart';
import 'dart:html' as html;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Minimal, Reproducible Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
class PrintReports extends StatelessWidget {
const PrintReports({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
child: Text(
"Print A Form . . ."
),
color: Colors.blue,
onPressed: () => {
// generate form . . .
// request submission . . .
// launch browser window . . .
},
),
],
);
}
Future launchURL(String download_url)
async {
try {
await html.window.open(download_url, "Get_Submission");
} catch (e) {
print(e);
}
}
}
This is test.dart:
import 'dart:html';
import 'package:flutter_tests/main.dart';
import 'package:test/test.dart';
void main(){
test("_launchURL launches browser at correct URL", () async {
// Arrange: setup the test
PrintReports printReports = PrintReports();
// Act
WindowBase window = await printReports.launchURL("https://someurl.com/download");
// Assert
expect(window, WindowBase);
});
}
To run the test I'm using this terminal command: pub run test test/print_reports_card_test.dart -p chrome.
The current test results are:
00:14 +2 -1: _launchURL launches browser at correct URL [E]
Expected: Type:<WindowBase>
Actual: <null>
[ . . . I can post this redacted output if you think it might be relevant . . . ]
00:14 +2 -1: Some tests failed.

Drop Down with sqlite - Flutter

I have database sqlite data and i want to show my data in drop down with changed id of my rows in table because in future i want to create another drop down to change to value of first drop down anyone can help ?
Working with SQLite on flutter
To gather the data from a SQLite database you could use the sqflite plugin (independently if is an iOS or Android device). You have to add the dependency to your pubspec.yaml.
dependencies:
...
sqflite: any
When you want to use sqflite you have to import the library.
import 'package:sqflite/sqflite.dart';
Next, you have to open a connection to SQLite, here we create a table in case you didn't have one
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)');
});
You can insert or retrieve data using database.rawQuery.
Insert:
int primaryKeyInsertedRow = await database.rawQuery('INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');
Select:
List<Map> list = await database.rawQuery('SELECT * FROM Test');
Remember to close the database when you are done with it.
await database.close();
Displaying a drop down menu list
For displaying the data you retrieve first you have to create a class that extends StatefulWidget, override the createState() method, and set your own state (in this example, SettingWidgetState)
#override
_SettingsWidgetState createState() => new _SettingsWidgetState();
Second you should define a state for it, defining a class that extends State<NameOfYourWidget>. In that class you should have a list of DropdownMenuItem<String> and a string member of the current selected element.
For the sake of convenience, in this example we are going to use a static list of cities:
List _cities = [
"Cluj-Napoca",
"Bucuresti",
"Timisoara",
"Brasov",
"Constanta"
];
Next, we override initState() setting our list of DropDownMenuItem to our list and the currently selected list element. After that we should call super.initState().
Also, we need to override the build() method. The goal is to return a Container that contains a DropDownButton, and that DropDownButton has assigned the list of items (defined in the class), the selected element (also defined in the class) and a event handler for the onChanged: property (here also are inserted additional widgets with the purpose of making it look nice)
#override
Widget build(BuildContext context) {
return new Container(
color: Colors.white,
child: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text("Please choose your city: "),
new Container(
padding: new EdgeInsets.all(16.0),
),
new DropdownButton(
value: _currentCity,
items: _dropDownMenuItems,
onChanged: changedDropDownItem,
)
],
)),
);
}
Lastly we define the method that is going to be called when a new item is selected from the list (changeDropDownItem(string selectedCity) in our example).
void changedDropDownItem(String selectedCity) {
setState(() {
_currentCity = selectedCity;
});
}
}
Link where I based my answer for drop down list. You can check out too getting started with sqflite plugin